package com.kpmg.datalake.accountcheck.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.plugins.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.kpmg.datalake.accountcheck.dao.AccountCheckCstMappingMapper;
import com.kpmg.datalake.accountcheck.dao.AccountCheckHandlerMapper;
import com.kpmg.datalake.accountcheck.dao.AccountCheckJounalMapper;
import com.kpmg.datalake.accountcheck.dao.AccountCheckPrtMapper;
import com.kpmg.datalake.accountcheck.dao.AccountCheckSubMapper;
import com.kpmg.datalake.accountcheck.dao.AccountCheckSubPrtMapper;
import com.kpmg.datalake.accountcheck.dao.AccountCourseMapper;
import com.kpmg.datalake.accountcheck.dto.AccountCheckExecuteResultDTO;
import com.kpmg.datalake.accountcheck.dto.AccountCheckReportConditionDTO;
import com.kpmg.datalake.accountcheck.dto.AccountCheckResultSaveParamDTO;
import com.kpmg.datalake.accountcheck.model.AccountCheckCstMapping;
import com.kpmg.datalake.accountcheck.model.AccountCheckPrt;
import com.kpmg.datalake.accountcheck.model.AccountCheckSubDetailMapping;
import com.kpmg.datalake.accountcheck.model.AccountCheckSubMapping;
import com.kpmg.datalake.accountcheck.model.AccountCheckSubPrt;
import com.kpmg.datalake.accountcheck.model.AccountCheckSubRow;
import com.kpmg.datalake.accountcheck.model.AccountCompareParam;
import com.kpmg.datalake.accountcheck.model.AccountCourse;
import com.kpmg.datalake.accountcheck.model.AccountOrigin;
import com.kpmg.datalake.accountcheck.model.KpmgChkactPrt;
import com.kpmg.datalake.accountcheck.model.KpmgChkactSrcdtl;
import com.kpmg.datalake.accountcheck.service.AccountCheckService;
import com.kpmg.datalake.accountcheck.service.IKpmgChkactPrtService;
import com.kpmg.datalake.accountcheck.service.IKpmgChkactSrcdtlService;
import com.kpmg.datalake.accountcheck.vo.AccountCheckChangeRowVO;
import com.kpmg.datalake.accountcheck.vo.AccountCheckComboVO;
import com.kpmg.datalake.accountcheck.vo.AccountCheckRowVO;
import com.kpmg.datalake.accountcheck.vo.AccountCheckSubQueryVO;
import com.kpmg.datalake.accountcheck.vo.AccountCheckSubVO;
import com.kpmg.datalake.accountcheck.vo.AccountCondition;
import com.kpmg.datalake.accountcheck.vo.AccountQueryVO;
import com.kpmg.datalake.accountcheck.vo.AccountRowDeleteVO;
import com.kpmg.datalake.accountcheck.vo.AccountRowVO;
import com.kpmg.datalake.accountcheck.vo.AccountSubCondition;
import com.kpmg.datalake.common.dynamicdatasource.DynamicDS;
import com.kpmg.datalake.common.model.ProjectRptSql;
import com.kpmg.datalake.common.service.CommonExecuteService;
import com.kpmg.datalake.common.service.IPrjRptSqlMpngService;
import com.kpmg.datalake.common.service.IProjectRptSqlService;
import com.kpmg.datalake.common.utils.ResponseUtils;
import com.kpmg.datalake.common.vo.GeneralHeaderVO;
import com.kpmg.datalake.common.vo.ServerResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFDataFormat;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;


/**
 * @author Echo
 * @create 2018-05-18
 * @desc 内部对账相关Service
 */
@Service
public class AccountCheckServiceImpl implements AccountCheckService {

  private static final Logger logger = LoggerFactory.getLogger(AccountCheckServiceImpl.class);

  @Autowired
  private AccountCourseMapper accountCourseMapper;

  @Autowired
  private AccountCheckHandlerMapper accountCheckHandlerMapper;

  @Autowired
  private AccountCheckPrtMapper accountCheckPrtMapper;

  @Autowired
  private AccountCheckSubMapper accountCheckSubMapper;

  @Autowired
  private AccountCheckCstMappingMapper accountCheckCstMappingMapper;

  @Autowired
  private AccountCheckJounalMapper accountCheckJounalMapper;

  @Autowired
  private AccountCheckSubPrtMapper accountCheckSubPrtMapper;

  @Autowired
  private IKpmgChkactSrcdtlService iKpmgChkactSrcdtlService;

  @Autowired
  private IKpmgChkactPrtService iKpmgChkactPrtService;

  @Autowired
  IProjectRptSqlService iProjectRptSqlService;

  @Autowired
  private IPrjRptSqlMpngService iPrjRptSqlMpngService;

  @Autowired
  private CommonExecuteService commonExecuteService;

  // 自动-相等抵消
  public static final String MATCH_AUTO_EQUAL = "0";
  // 手工-相等抵消
  public static final String MATCH_MANUAL_EQUAL = "1";
  // 手工-抵小抵消
  public static final String MATCH_MANUAL_OFFSET = "2";
  // 自动-抵小抵消
  public static final String MATCH_AUTO_OFFSET = "3";
  // 单边记账
  public static final String MATCH_UNILATERAL_ACCOUNT = "4";
  // 科目未配置
  public static final String MATCH_SUBJECT_UNDEFINED = "7";
  // MTCH_IND 匹配状态 未匹配
  public static final int KPMG_SRCDTL_MTCH_IND_UNMATCH = 0;
  // MTCH_IND 匹配状态 已匹配
  public static final int KPMG_SRCDTL_MTCH_IND_MATCH = 1;
  // RSLT_CD column: 本方
  public static final int KPMG_CHK_ACCOUNT_SELF_TYPE = 0;
  // RSLT_CD column: 对方
  public static final int KPMG_CHK_ACCOUNT_CNTR_TYPE = 1;

  // 月份数组常量
  private static final String[] YEARARR =
      {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};

  public static final String EXCEPTION_MESSAGE_QUERY_TABLE_PREFIX = "查询列表时出现异常:";

  public static final String UNDERLINE_SEPARATOR = "_";
  /**
   * 匹配类型 - 资产->负债
   */
  public static final String SBJ_MAPPING_TYPE_ZF = "0";
  /**
   * 匹配类型 - 收入->成本+费用
   */
  public static final String SBJ_MAPPING_TYPE_SC = "1";

  /**
   * 匹配 - 本方
   */
  public static final int SBJ_RSLT_CD_SELF = 0;

  /**
   * 匹配 - 对方
   */
  public static final int SBJ_RSLT_CD_CNTL = 1;

  public static final String HAS_NEXT_PAGE = "hasNextPage";

  public static final String ACCOUNT_SIZE = "accountSize";

  /**
   * 获取科目类型列表
   *
   * @return
   */
  @Override
  @DynamicDS("projectId")
  public ServerResponse<PageInfo> getCourseList(int page, int size, String projectId, String corpCode,
      String queryParam) {

    PageHelper.startPage(page, size);
    List<AccountCourse> result = accountCourseMapper.queryCouseList(corpCode, queryParam);
    PageInfo pageData = new PageInfo<>(result);

    return ServerResponse.createBySuccess("获取科目列表成功", pageData);

  }

  private boolean isStringNotBlank(String str) {
    return org.apache.commons.lang3.StringUtils.isNotBlank(str);
  }

  /**
   * 内部对账导出请求处理，生成需要导出的对象集合
   *
   * @param projectId
   * @param fileName
   * @param accountQueryVO
   * @param request
   * @param response
   */
  @SuppressWarnings("unchecked")
  @Override
  @DynamicDS("projectId")
  public void exportAccountReport(String projectId, String fileName, AccountQueryVO accountQueryVO,
      HttpServletRequest request, HttpServletResponse response) {

    String[] startArr = accountQueryVO.getStartDate().split("/");
    String[] endArr = accountQueryVO.getEndDate().split("/");

    accountQueryVO.setYear(endArr[0]);
    accountQueryVO.setBeginMonth(startArr[1]);
    accountQueryVO.setEndMonth(endArr[1]);
    List<AccountCheckComboVO> reportData =
        this.getChkAccountDataList(accountQueryVO, -1, accountQueryVO.getOffsetType());

    List<AccountRowVO> rowList = new ArrayList<>();
    for (AccountCheckComboVO item : reportData) {
      String flowNo = item.getFlowNo();
      AccountRowVO row = new AccountRowVO();
      List<AccountCheckRowVO> hostList = item.getHostList();
      AccountCheckRowVO accountCheckRow = hostList.get(0);
      row.setFlowNo(flowNo);
      row.setOwnerName(accountCheckRow.getOwnerName());
      row.setTradeName(accountCheckRow.getTradeName());
      row.setCourseType(accountCheckRow.getCourseType());
      row.setBookMoney(accountCheckRow.getBookMoney());
      row.setOffsetMoney(accountCheckRow.getOffsetMoney());

      String diffMoney = accountCheckRow.getDiffMoney();
      boolean isDiffMoneyNotBlank = this.isStringNotBlank(diffMoney);
      String diffMonryStr = "0";
      if (isDiffMoneyNotBlank) {
        diffMonryStr = diffMoney;
      }
      row.setDiffMoney(diffMonryStr);

      String typeFlag = accountCheckRow.getOffsetTypeName();
      row.setOffsetType(typeFlag);
      row.setRemark(accountCheckRow.getRemark());
      row.setBookTotal(accountCheckRow.getBookTotal());
      row.setOffsetTotal(accountCheckRow.getOffsetTotal());
      row.setDiffTotal(accountCheckRow.getDiffTotal());
      row.setDeleteFlag(accountCheckRow.isDeleteFlag());
      row.setMappingName(accountCheckRow.getMappingName());
      List<AccountRowVO> hostRowList = new ArrayList<>();
      for (int i = 0; i < hostList.size(); i++) {
        AccountCheckRowVO host = hostList.get(i);
        AccountRowVO row2 = new AccountRowVO();
        row2.setOwnerName(host.getOwnerName());
        row2.setTradeName(host.getTradeName());
        row2.setCourseType(host.getCourseType());
        row2.setBookMoney(host.getBookMoney());
        row2.setOffsetMoney(host.getOffsetMoney());
        String diffMoney2 = host.getDiffMoney();
        boolean isHostDiffMoneyNotBlank = this.isStringNotBlank(diffMoney2);
        String hostDiffMoneyStr = "0";
        if (isHostDiffMoneyNotBlank) {
          hostDiffMoneyStr = diffMoney2;
        }
        row2.setDiffMoney(hostDiffMoneyStr);

        String typeFlag2 = host.getOffsetTypeName();

        row2.setOffsetType(typeFlag2);
        row2.setRemark(host.getRemark());
        row2.setDeleteFlag(host.isDeleteFlag());
        row2.setMappingName(host.getMappingName());
        hostRowList.add(row2);
      }
      row.setHostList(hostRowList);

      List<AccountCheckRowVO> tradeList = item.getTradeList();
      List<AccountRowVO> tradeRowList = new ArrayList<>();
      for (int i = 0; i < tradeList.size(); i++) {
        AccountCheckRowVO tradeMap = tradeList.get(i);
        AccountRowVO row2 = new AccountRowVO();
        row2.setOwnerName(tradeMap.getOwnerName());
        row2.setTradeName(tradeMap.getTradeName());
        row2.setCourseType(tradeMap.getCourseType());
        row2.setBookMoney(tradeMap.getBookMoney());
        row2.setOffsetMoney(tradeMap.getOffsetMoney());
        String diffMoney2 = tradeMap.getDiffMoney();
        boolean isTradeDiffMoneyNotBlank = this.isStringNotBlank(diffMoney2);
        String tradeDiffMoneyStr = "0";
        if (isTradeDiffMoneyNotBlank) {
          tradeDiffMoneyStr = diffMoney2;
        }
        row2.setDiffMoney(tradeDiffMoneyStr);

        String typeFlag2 = tradeMap.getOffsetTypeName();

        row2.setOffsetType(typeFlag2);
        row2.setRemark(tradeMap.getRemark());
        row2.setDeleteFlag(tradeMap.isDeleteFlag());
        row2.setMappingName(tradeMap.getMappingName());
        tradeRowList.add(row2);
      }

      row.setTradeList(tradeRowList);
      rowList.add(row);
    }

    this.generateExportResponse(fileName, accountQueryVO, request, response, rowList);

  }

  /**
   * 内部对账报表导出
   *
   * @param fileName
   * @param accountQueryVO
   * @param request
   * @param response
   * @param rowList
   */
  private void generateExportResponse(String fileName, AccountQueryVO accountQueryVO,
      HttpServletRequest request, HttpServletResponse response, List<AccountRowVO> rowList) {
    InputStream in = getClass().getClassLoader().getResourceAsStream("templateAccount.xlsx");

    XSSFWorkbook workbook = null;
    OutputStream out = null;
    try {
      workbook = new XSSFWorkbook(in);
      workbook.setSheetName(0,
          "数据报表" + (new SimpleDateFormat("yyyyMMdd")).format(new Date()).trim());
      XSSFSheet sheet = workbook.getSheetAt(0);
      XSSFCellStyle cellStyle = workbook.createCellStyle();
      XSSFDataFormat dataFormat = workbook.createDataFormat();
      cellStyle.setDataFormat(
          dataFormat.getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"\"-\"\"??_);_(@_)"));
      // 插入初始行
      sheet.getRow(1).createCell(1)
          .setCellValue(accountQueryVO.getCorpCd() + " " + accountQueryVO.getCorpNm().trim());
      sheet.getRow(2).createCell(1).setCellValue(accountQueryVO.getStartDate());
      sheet.getRow(3).createCell(1).setCellValue(accountQueryVO.getEndDate());
      sheet.getRow(4).createCell(1)
          .setCellValue((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()).trim());

      int rowNum = 7;
      Row row = null;
      Cell cell0 = null;
      Cell cell1 = null;
      Cell cell2 = null;
      Cell cell3 = null;
      Cell cell4 = null;
      Cell cell5 = null;
      Cell cell6 = null;
      Cell cell7 = null;
      Cell cell8 = null;
      Cell cell9 = null;
      for (AccountRowVO item : rowList) {

        List<AccountRowVO> hostList = item.getHostList();

        for (AccountRowVO host : hostList) {
          row = sheet.createRow(rowNum);

          cell0 = row.createCell(0);
          cell0.setCellValue(item.getFlowNo());

          cell1 = row.createCell(1);
          cell1.setCellValue(host.getOwnerName());

          cell2 = row.createCell(2);
          cell2.setCellValue(host.getTradeName());

          cell3 = row.createCell(3);
          cell3.setCellValue(host.getCourseType());

          cell4 = row.createCell(4);
          cell4.setCellStyle(cellStyle);
          cell4.setCellValue(Double.parseDouble(host.getBookMoney()));

          cell5 = row.createCell(5);
          cell5.setCellStyle(cellStyle);
          cell5.setCellValue(Double.parseDouble(host.getOffsetMoney()));

          cell6 = row.createCell(6);
          cell6.setCellStyle(cellStyle);
          cell6.setCellValue(Double.parseDouble(host.getDiffMoney()));

          cell7 = row.createCell(7);
          cell7.setCellValue(host.getOffsetType());

          cell8 = row.createCell(8);
          cell8.setCellValue(host.getMappingName());
          cell9 = row.createCell(9);
          cell9.setCellValue(host.getRemark());
          rowNum++;
        }

        List<AccountRowVO> tradeList = item.getTradeList();
        for (AccountRowVO trade : tradeList) {
          row = sheet.createRow(rowNum);

          cell0 = row.createCell(0);
          cell0.setCellValue(item.getFlowNo());

          cell1 = row.createCell(1);
          cell1.setCellValue(trade.getOwnerName());

          cell2 = row.createCell(2);
          cell2.setCellValue(trade.getTradeName());

          cell3 = row.createCell(3);
          cell3.setCellValue(trade.getCourseType());

          cell4 = row.createCell(4);
          cell4.setCellStyle(cellStyle);
          cell4.setCellValue(Double.parseDouble(trade.getBookMoney()));

          cell5 = row.createCell(5);
          cell5.setCellStyle(cellStyle);
          cell5.setCellValue(Double.parseDouble(trade.getOffsetMoney()));

          cell6 = row.createCell(6);
          cell6.setCellStyle(cellStyle);
          cell6.setCellValue(Double.parseDouble(trade.getDiffMoney()));

          cell7 = row.createCell(7);
          cell7.setCellValue(trade.getOffsetType());

          cell8 = row.createCell(8);
          cell8.setCellValue(trade.getMappingName());

          cell9 = row.createCell(9);
          cell9.setCellValue(trade.getRemark());
          rowNum++;
        }

        boolean hasTotalRow =
            "自动-单边记账".equals(item.getOffsetType()) && !CollectionUtils.isEmpty(tradeList)
                || !"自动-单边记账".equals(item.getOffsetType()) && !"科目未配置".equals(item.getOffsetType());
        if (hasTotalRow) {
          row = sheet.createRow(rowNum);

          cell0 = row.createCell(0);
          cell0.setCellValue("差额");

          cell1 = row.createCell(1);
          cell1.setCellValue(item.getOwnerName());

          cell2 = row.createCell(2);
          cell2.setCellValue(item.getTradeName());

          cell4 = row.createCell(4);
          cell4.setCellStyle(cellStyle);
          cell4.setCellValue(Double.parseDouble(item.getBookTotal()));

          cell5 = row.createCell(5);
          cell5.setCellStyle(cellStyle);
          cell5.setCellValue(Double.parseDouble(item.getOffsetTotal()));

          cell6 = row.createCell(6);
          cell6.setCellStyle(cellStyle);
          cell6.setCellValue(Double.parseDouble(item.getDiffTotal()));

          cell7 = row.createCell(7);
          cell7.setCellValue(item.getOffsetType());

          cell8 = row.createCell(8);
          cell8.setCellValue(item.getMappingName());

          rowNum = rowNum + 2;
        }

      }
      // 设置文件响应头
      ResponseUtils.setZipDownloadResponseHeader(fileName, request, response);
      out = response.getOutputStream();
      workbook.write(out);
      out.flush();
      out.close();

    } catch (IOException e) {
      logger.error("导出内部对账报表数据时出现异常:" + e.getMessage(), e);
      closeOutStream(out, "导出内部对账报表数据时关闭流出现异常:");
    }
  }

  private void closeOutStream(OutputStream out, String errorMessage) {
    if (out != null) {
      try {
        out.close();
      } catch (IOException e) {
        logger.error(errorMessage + e.getMessage(), e);
      }
    }
  }

  @Override
  public ServerResponse getChkAccountData(String username, String projectId,
      AccountQueryVO accountQueryVO, int num) {
    return ((AccountCheckServiceImpl) AopContext.currentProxy()).getChkAccountData(projectId,
        accountQueryVO, num, "-1");
  }


  /**
   * 根据session中的用户/项目Id和对应报告的参数,获得内部对账数据
   *
   * @param projectId 项目Id
   * @param accountQueryVO 报告的条件
   * @param num 用户分页控制, -1表示不分页
   * @return
   */
  @Override
  @DynamicDS("projectId")
  public ServerResponse<JSONObject> getChkAccountData(String projectId, AccountQueryVO accountQueryVO, int num,
      String offsetType) {

    List<AccountCheckComboVO> records = this.getChkAccountDataList(accountQueryVO, num, offsetType);
    JSONObject result = new JSONObject();
    result.put("list", records);
    result.put("more", true);
    // 是否进行分页懒加载
    if (records.isEmpty()) {
      result.put("more", false);
    }
    return ServerResponse.createBySuccess("获取对账分录信息成功！", result);

  }

  /**
   * 根据session中的用户/项目Id和对应报告的参数,获得内部对账数据
   *
   * @param accountQueryVO 报告的条件
   * @param num 用户分页控制, -1表示不分页
   * @return
   */
  private List<AccountCheckComboVO> getChkAccountDataList(AccountQueryVO accountQueryVO, int num,
      String offsetType) {

    Map<String, Map<String, List<AccountCheckRowVO>>> batchIdRelationshipListMap =
        this.queryChkActPrtRecords(accountQueryVO, num, offsetType);

    Map<String, List<AccountCheckRowVO>> manualChkListMap =
        this.queryManualChkAccountData(accountQueryVO);

    return matchChkData(batchIdRelationshipListMap, manualChkListMap, accountQueryVO, num);

  }

  /**
   * 从KPMG_CHKACT_PRT table中获取本方和对方的信息
   *
   * @param accountQueryVO 主要包含year, endMonth
   * @param num 用于分页控制, -1表示不分页
   * @param offsetType 对账类型, 当作取数据的条件
   * @return 批次号(MTCH_BTCH_ID, alias mtchBtchId) --> 本方/对方(RSLT_CD, alias chkType, 0本方/1对方) -->
   *         List<Map<String, Object>>(KPMG_CHKACT_PRT表中数据对象的集合)
   */
  private Map<String, Map<String, List<AccountCheckRowVO>>> queryChkActPrtRecords(
      AccountQueryVO accountQueryVO, int num, String offsetType) {

    List<AccountCheckPrt> resultList = null;
    AccountCheckReportConditionDTO reportCondition = new AccountCheckReportConditionDTO();
    // 参考initParam()方法
    reportCondition.setAcgYr(accountQueryVO.getYear());
    reportCondition.setAcgMo(accountQueryVO.getEndMonth());
    reportCondition.setCorpCd(accountQueryVO.getCorpCd());
    reportCondition.setMappingId(accountQueryVO.getMappingId());
    reportCondition.setOffsetType(offsetType);
    try {
      if (num == -1) {
        resultList = accountCheckPrtMapper.selectAccountCheckReport(reportCondition);
      } else {
        // 分页逻辑如下, 由于报表是基于本方数据, 然后根据本方数据的批次号找到相应的对方数据和手动录入的数据, 因此分页需要在本方数据上操作.
        int start = (accountQueryVO.getPageNum() - 1) * accountQueryVO.getPageSize() + 1;
        int end = accountQueryVO.getPageNum() * accountQueryVO.getPageSize();
        reportCondition.setStart(start);
        reportCondition.setEnd(end);
        resultList = accountCheckPrtMapper.selectAccountCheckReportByPage(reportCondition);
      }
    } catch (Exception e) {
      logger.error(EXCEPTION_MESSAGE_QUERY_TABLE_PREFIX + e.getMessage(), e);
      return null;
    }

    return this.convertAccountCheckModelListToVOMap(resultList);
  }

  /**
   * 将数据库集合加工并转化为对应形式的VO Map集合
   *
   * @param resultList
   * @return
   */
  private Map<String, Map<String, List<AccountCheckRowVO>>> convertAccountCheckModelListToVOMap(
      List<AccountCheckPrt> resultList) {
    if (CollectionUtils.isEmpty(resultList)) {
      return null;
    }
    Map<String, Map<String, List<AccountCheckRowVO>>> batchIdRelationshipListMap = new TreeMap<>();
    AccountCheckRowVO accountCheckRow = null;

    // 获取科目匹配映射关系，并组装成 id -> 映射名称组成的map
    List<AccountCheckSubMapping> mappingList = accountCheckSubMapper.getSubMatchList(null);
    Map<String, String> rulesMap = new HashMap();
    // 设置未匹配和单边记账的键值
    rulesMap.put("0", "");
    for (AccountCheckSubMapping item : mappingList) {
      rulesMap.put(item.getMatchId(), item.getSubMap());
    }

    for (AccountCheckPrt accountCheck : resultList) {
      accountCheckRow = new AccountCheckRowVO();
      accountCheckRow.setOwnerName(accountCheck.getCorpNm());
      accountCheckRow.setTradeName(accountCheck.getCstStdNm());// 修改使用standardName
      // S.SBJ_NM as courseType
      accountCheckRow.setCourseType(accountCheck.getSbjNm());
      // S.AMT_EOP as bookMoney
      accountCheckRow.setBookMoney(accountCheck.getAmtEop().toString());
      // S.AMT_OFST as offsetMoney
      accountCheckRow.setOffsetMoney(accountCheck.getAmtOfst().toString());
      // S.AMT_DIF as diffMoney
      accountCheckRow.setDiffMoney(accountCheck.getAmtDif().toString());
      // S.MTCH_TP_NM as offsetType
      accountCheckRow.setOffsetType(accountCheck.getMtchTpNm());
      // S.MTCH_RMRK as remark
      String remark = accountCheck.getMtchRmrk();
      accountCheckRow.setRemark(remark == null ? "" : remark);
      // S.CORP_CD as corpCode
      accountCheckRow.setCorpCode(accountCheck.getCorpCd());
      // S.CST_CD as cstCode
      accountCheckRow.setCstCode(accountCheck.getCstCd());
      // S.MTCH_BTCH_ID as mtchBtchId
      String mtchBtchId = accountCheck.getMtchBtchId();
      accountCheckRow.setMtchBtchId(mtchBtchId);
      // S.RSLT_CD as chkType
      String rsltCd = accountCheck.getRsltCd();
      accountCheckRow.setChkType(rsltCd);
      // S.AMT_EOP_ACM as bookTotal
      accountCheckRow.setBookTotal(accountCheck.getAmtEopAcm().toString());
      // S.AMT_OFST_ACM as offsetTotal
      accountCheckRow.setOffsetTotal(accountCheck.getAmtOfstAcm().toString());
      // S.AMT_EOP_ACM - S.AMT_OFST_ACM as diffTotal
      BigDecimal diffTotal = accountCheck.getAmtEopAcm().subtract(accountCheck.getAmtOfstAcm());
      accountCheckRow.setDiffTotal(diffTotal.toString());
      accountCheckRow.setDeleteFlag(false);
      // 设置匹配规则名称
      accountCheckRow.setMappingName(rulesMap.get(accountCheck.getMappingId()));

      if (!batchIdRelationshipListMap.containsKey(mtchBtchId)) {
        batchIdRelationshipListMap.put(mtchBtchId, new TreeMap<String, List<AccountCheckRowVO>>());
      }
      Map<String, List<AccountCheckRowVO>> relationshipListMap =
          batchIdRelationshipListMap.get(mtchBtchId);

      if (!relationshipListMap.containsKey(rsltCd)) {
        relationshipListMap.put(rsltCd, new ArrayList<AccountCheckRowVO>());
      }
      List<AccountCheckRowVO> listMap = relationshipListMap.get(rsltCd);

      listMap.add(accountCheckRow);
    }

    return batchIdRelationshipListMap;
  }

  /**
   * 从KPMG_CHKACT_CNTRPRT_MNL表中获取手动录入的数据
   *
   * @param accountQueryVO 主要包含year, endMonth
   * @return 返回 MTCH_BTCH_ID(alias mtchBtchId) --> List<Map<String,
   *         Object>>(表KPMG_CHKACT_CNTRPRT_MNL中对应数据的集合)
   */
  private Map<String, List<AccountCheckRowVO>> queryManualChkAccountData(
      AccountQueryVO accountQueryVO) {

    AccountCheckReportConditionDTO reportCondition = new AccountCheckReportConditionDTO();
    // 参考initParam()方法
    reportCondition.setAcgYr(accountQueryVO.getYear());
    reportCondition.setAcgMo(accountQueryVO.getEndMonth());
    reportCondition.setMappingId(accountQueryVO.getMappingId());
    List<AccountCheckPrt> resultList = null;

    try {
      resultList = accountCheckPrtMapper.selectAccountCheckMnlReport(reportCondition);
    } catch (Exception e) {
      logger.error("获取手工数据异常:{}", e.getMessage());
      return null;
    }

    return convertAccountCheckMnlModelListToVOMap(resultList);
  }

  /**
   * 将手工数据从model集合转化为VO形式的Map
   *
   * @param resultList
   * @return
   */
  private Map<String, List<AccountCheckRowVO>> convertAccountCheckMnlModelListToVOMap(
      List<AccountCheckPrt> resultList) {
    if (CollectionUtils.isEmpty(resultList)) {
      return null;
    }
    // 使用 MTCH_BTCH_ID 作为Key -- list
    Map<String, List<AccountCheckRowVO>> keyChkListMap = new HashMap<>();
    AccountCheckRowVO accountCheckRow = null;
    for (AccountCheckPrt accountCheck : resultList) {
      accountCheckRow = new AccountCheckRowVO();
      accountCheckRow.setChkId(accountCheck.getChkId());
      // CH.CORP_NM as ownerName
      accountCheckRow.setOwnerName(accountCheck.getCorpNm());
      // CH.CST_NM as tradeName
      accountCheckRow.setTradeName(accountCheck.getCstNm());
      // SBJ_NM as courseType
      accountCheckRow.setCourseType(accountCheck.getSbjNm());
      // AMT_EOP as bookMoney
      accountCheckRow.setBookMoney(accountCheck.getAmtEop().toString());
      // AMT_OFST as offsetMoney
      accountCheckRow.setOffsetMoney(accountCheck.getAmtOfst().toString());
      // AMT_DIF as diffMoney
      BigDecimal diffMoney = accountCheck.getAmtDif();
      accountCheckRow.setDiffMoney(diffMoney != null ? diffMoney.toString() : "0");
      // MTCH_TP_NM as offsetType
      accountCheckRow.setOffsetType(accountCheck.getMtchTpNm());
      // MTCH_RMRK as remark
      String remark = accountCheck.getMtchRmrk();
      accountCheckRow.setRemark(remark == null ? "" : remark);
      // CORP_CD as corpCode
      accountCheckRow.setCorpCode(accountCheck.getCorpCd());
      // CST_CD as cstCode
      accountCheckRow.setCstCode(accountCheck.getCstCd());
      // MTCH_BTCH_ID as mtchBtchId
      String mtchBtchId = accountCheck.getMtchBtchId();
      accountCheckRow.setMtchBtchId(mtchBtchId);
      accountCheckRow.setDeleteFlag(true);

      if (!keyChkListMap.containsKey(mtchBtchId)) {
        keyChkListMap.put(mtchBtchId, new ArrayList<AccountCheckRowVO>());
      }
      List<AccountCheckRowVO> valueList = keyChkListMap.get(mtchBtchId);
      valueList.add(accountCheckRow);
    }
    return keyChkListMap;
  }


  /**
   * 将手工录入数据匹配到本方和对方的账目数据中 匹配根据MTCH_BTCH_ID,找到手动录入的数据, 然后将数据加载到账目的对方集合中
   *
   * @param manualChkListMap
   * @param accountQueryVO
   * @param num 用于控制分页 -1表示不分页
   * @return
   */
  private List<AccountCheckComboVO> matchChkData(
      Map<String, Map<String, List<AccountCheckRowVO>>> batchIdRelationshipListMap,
      Map<String, List<AccountCheckRowVO>> manualChkListMap, AccountQueryVO accountQueryVO,
      int num) {

    // Use TreeMap with mtch_tp_nm as key, so we can sort the result based on it.
    // After generate the TreeMap, loop its values, add the flow_no to each object
    // and store into records
    Map<String, List<AccountCheckComboVO>> mtchTpNmResultListMap = new TreeMap<>();

    if (batchIdRelationshipListMap == null) {
      return new ArrayList<>();
    }

    AccountCheckComboVO accountCheckTotal = null;

    for (Map.Entry<String, Map<String, List<AccountCheckRowVO>>> entry : batchIdRelationshipListMap
        .entrySet()) {
      String mtchBtchId = entry.getKey();
      Map<String, List<AccountCheckRowVO>> relationshipListMap = entry.getValue();
      List<AccountCheckRowVO> selfList = relationshipListMap.get(KPMG_CHK_ACCOUNT_SELF_TYPE + "");
      List<AccountCheckRowVO> cntrList = relationshipListMap.get(KPMG_CHK_ACCOUNT_CNTR_TYPE + "");

      if (CollectionUtils.isEmpty(selfList)) {
        // 如果没有本方记录, 这是不合理的
        continue;
      }

      accountCheckTotal = new AccountCheckComboVO();
      this.generateAccountCheckComboVO(accountCheckTotal, mtchBtchId, selfList, cntrList,
          manualChkListMap);
      String mtchTpNm = accountCheckTotal.getType();
      if (!mtchTpNmResultListMap.containsKey(mtchTpNm)) {
        mtchTpNmResultListMap.put(mtchTpNm, new ArrayList<AccountCheckComboVO>());
      }
      mtchTpNmResultListMap.get(mtchTpNm).add(accountCheckTotal);
    }

    return this.generateResultList(accountQueryVO, num, mtchTpNmResultListMap);

  }

  /**
   * 插入分录号名称
   *
   * @param accountQueryVO
   * @param num
   * @param mtchTpNmResultListMap
   * @return
   */
  private List<AccountCheckComboVO> generateResultList(AccountQueryVO accountQueryVO, int num,
      Map<String, List<AccountCheckComboVO>> mtchTpNmResultListMap) {
    List<AccountCheckComboVO> records = new LinkedList<>();
    int number = 1;
    if (num != -1) {
      // 分页, 此时分录的名称需要根据当前页设置
      number = (accountQueryVO.getPageNum() - 1) * accountQueryVO.getPageSize() + 1;
    }

    for (List<AccountCheckComboVO> valueList : mtchTpNmResultListMap.values()) {
      for (AccountCheckComboVO object : valueList) {
        object.setFlowNo("分录" + number);
        number++;
        records.add(object);
      }
    }
    return records;
  }

  /**
   *
   * @param accountCheckTotal
   * @param mtchBtchId
   * @param selfList
   * @param cntrList
   * @param manualChkListMap
   */
  private void generateAccountCheckComboVO(AccountCheckComboVO accountCheckTotal, String mtchBtchId,
      List<AccountCheckRowVO> selfList, List<AccountCheckRowVO> cntrList,
      Map<String, List<AccountCheckRowVO>> manualChkListMap) {

    AccountCheckRowVO firstHost = selfList.get(0);
    String mtchTpNm = firstHost.getOffsetType();
    BigDecimal offsetTotal = new BigDecimal("0");
    BigDecimal bookTotal = new BigDecimal("0");
    BigDecimal diffTotal = new BigDecimal("0");

    BigDecimal tempOffsetTotal = new BigDecimal(firstHost.getOffsetTotal());
    BigDecimal tempBookTotal = new BigDecimal(firstHost.getBookTotal());
    BigDecimal tempDiffTotal = new BigDecimal(firstHost.getDiffTotal());

    offsetTotal = offsetTotal.add(tempOffsetTotal);
    bookTotal = bookTotal.add(tempBookTotal);
    diffTotal = diffTotal.add(tempDiffTotal);

    List<AccountCheckRowVO> tradeList = new ArrayList<>();
    if (cntrList != null) {
      tradeList.addAll(cntrList);
    }

    if (manualChkListMap != null && manualChkListMap.containsKey(mtchBtchId)) {
      List<AccountCheckRowVO> manualCntrList = manualChkListMap.get(mtchBtchId);
      if (manualCntrList != null) {
        tradeList.addAll(manualCntrList);
      }
    }

    int accountSize = selfList.size() + tradeList.size();
    accountCheckTotal.setOffsetTotal(offsetTotal.toString());
    accountCheckTotal.setBookTotal(bookTotal.toString());
    accountCheckTotal.setDiffTotal(diffTotal.toString());
    accountCheckTotal.setHostList(selfList);
    accountCheckTotal.setTradeList(tradeList);
    accountCheckTotal.setAccountSize(accountSize);
    accountCheckTotal.setRemark("");
    accountCheckTotal.setBatchId(firstHost.getMtchBtchId());
    accountCheckTotal.setType(mtchTpNm);
    accountCheckTotal.setTotalCstNm(firstHost.getTradeName());
    accountCheckTotal.setTotalCstCode(firstHost.getCstCode());
    accountCheckTotal.setTotalCorpNm(firstHost.getOwnerName());
    accountCheckTotal.setTotalCorpCode(firstHost.getCorpCode());

    // 单边记账,15号之前先不让添加 ---改 aimee单边记账添加
    if (!MATCH_AUTO_EQUAL.equals(mtchTpNm) && !MATCH_SUBJECT_UNDEFINED.equals(mtchTpNm)) {
      accountCheckTotal.setManualOper(true);
    }
    if (MATCH_UNILATERAL_ACCOUNT.equals(mtchTpNm) && !CollectionUtils.isEmpty(tradeList)
        || !MATCH_UNILATERAL_ACCOUNT.equals(mtchTpNm)
        && !MATCH_SUBJECT_UNDEFINED.equals(mtchTpNm)) {
      accountCheckTotal.setDisplayTotal(true);
    }

  }

  @Override
  @DynamicDS("projectId")
  public synchronized ServerResponse<JSONObject> addManualRow(AccountCondition accountCondition,
      String projectId) {

    accountCondition.setRsltCd(KPMG_CHK_ACCOUNT_SELF_TYPE);
    List<AccountCheckPrt> hostChkActList = this.getChkActData(accountCondition);
    if (!CollectionUtils.isEmpty(hostChkActList)) {
      // 获取本方匹配记录
      AccountCheckPrt hostObj = hostChkActList.get(0);
      // 1. 判断本方是谁
      // 1 如果本方是自己，增加本方记录，增加差额 代码暂时删除

      // 2. 如果本方是交易对手，减少本方中的AMT_DIF 字段的金额
      boolean updateSuccess = this.processAccountCheckAdjustment(accountCondition, hostObj);
      if (!updateSuccess) {
        return ServerResponse.createByErrorMessage("新增手动数据失败！");
      }

    }

    List<AccountCheckComboVO> reportData = new ArrayList<>();
    // 移除 RSLT_CD的值, 因为accountDataComboFetch方法会取所有数据
    accountCondition.setRsltCd(null);
    AccountCheckComboVO accountDetailDataMap =
        accountDataComboFetch(accountCondition, 0, accountCondition.getAccountName());
    reportData.add(accountDetailDataMap);

    JSONObject result = new JSONObject();
    result.put("list", reportData);
    result.put(ACCOUNT_SIZE, accountDetailDataMap.getAccountSize());

    return ServerResponse.createBySuccess("新增手工记录成功", result);

  }

  /**
   * 根据批次Id和本方/对方标识,获取数据
   *
   * @param accountCondition 使用其中的mtchBtchId和RSTL_CD
   * @return
   */
  public List<AccountCheckPrt> getChkActData(AccountCondition accountCondition) {
    return accountCheckPrtMapper.selectByBatchIdAndRsltCd(accountCondition);
  }

  @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000,
      rollbackFor = Exception.class)
  public boolean processAccountCheckAdjustment(AccountCondition accountCondition,
      AccountCheckPrt hostObj) {
    // 插入金额
    BigDecimal addMoney = new BigDecimal(accountCondition.getAmtOfst());
    // 差异金额
    BigDecimal difMoney = hostObj.getAmtDif();
    // 抵消金额
    BigDecimal amtOfst = hostObj.getAmtOfst();
    // total 记账金额
    BigDecimal amtEopAcm = hostObj.getAmtEopAcm();
    // total 抵消金额
    BigDecimal amtOfstAcm = hostObj.getAmtOfstAcm();

    AccountCheckPrt selfAccountCheckPrt = new AccountCheckPrt();
    // 根据MTCH_BTCH_ID为条件 更新KPMG_CHKACT_PRT-对账结果表（本方记录， 金额差异AMT_DIF需要变化, 抵消金额 + 新增手动金额,
    // total金额/total抵消金额 - 新增手动金额
    selfAccountCheckPrt.setAcgYr(accountCondition.getAcgYr());
    selfAccountCheckPrt.setAcgMo(accountCondition.getAcgMo());
    selfAccountCheckPrt.setMtchBtchId(hostObj.getMtchBtchId());
    selfAccountCheckPrt.setRsltCd(KPMG_CHK_ACCOUNT_SELF_TYPE + "");
    selfAccountCheckPrt.setAmtDif(difMoney.subtract(addMoney));
    selfAccountCheckPrt.setAmtOfst(amtOfst.add(addMoney));
    selfAccountCheckPrt.setAmtEopAcm(amtEopAcm.subtract(addMoney));
    selfAccountCheckPrt.setAmtOfstAcm(amtOfstAcm.subtract(addMoney));
    if ((MATCH_UNILATERAL_ACCOUNT).equals(hostObj.getMtchTpNm())) {
      selfAccountCheckPrt.setMtchTpNm(MATCH_AUTO_OFFSET);
    }

    AccountCheckPrt cntlAccountCheckPrt = new AccountCheckPrt();
    // 根据MTCH_BTCH_ID, RSLT_CD = 1 更新KPMG_CHKACT_PRT-对账结果表（对方记录，自动, total 记账金额 -
    // 新增手动金额, total 抵消金额 - 新增手动金额
    cntlAccountCheckPrt.setAcgYr(accountCondition.getAcgYr());
    cntlAccountCheckPrt.setAcgMo(accountCondition.getAcgMo());
    cntlAccountCheckPrt.setMtchBtchId(hostObj.getMtchBtchId());
    cntlAccountCheckPrt.setRsltCd(KPMG_CHK_ACCOUNT_CNTR_TYPE + "");
    cntlAccountCheckPrt.setAmtEopAcm(amtEopAcm.subtract(addMoney));
    cntlAccountCheckPrt.setAmtOfstAcm(amtOfstAcm.subtract(addMoney));

    if (addMoney.compareTo(difMoney) == 0) {
      // 状态变为1
      selfAccountCheckPrt.setMtchTpNm(MATCH_MANUAL_EQUAL);
    } else if (addMoney.compareTo(difMoney) < 0) {
      logger.info("Account Check amount less than");
    } else {
      logger.info("Account Check amount great than");
    }

    accountCondition.setMtchTpNm(MATCH_MANUAL_OFFSET);
    String uuid = this.getUUID();
    AccountCheckPrt accountCheckPrtMnl =
        convertAccountConditionToAccountCheckPrt(accountCondition, uuid);

    try {
      int updatedSelfNum = accountCheckPrtMapper.updateAccountCheckPrt(selfAccountCheckPrt);
      int updatedCntlNum = accountCheckPrtMapper.updateAccountCheckPrt(cntlAccountCheckPrt);
      int insertMnlNum = accountCheckPrtMapper.insertAccountCheckPrtMnl(accountCheckPrtMnl);
      if (logger.isDebugEnabled()) {
        logger.debug("Update {} data for Self AccountCheckPrt", updatedSelfNum);
        logger.debug("Update {} data for Cntl AccountCheckPrt", updatedCntlNum);
        logger.debug("Insert {} data for Mnl AccountCheckPrt", insertMnlNum);
      }
    } catch (Exception e) {
      logger.error("新增手动录入数据失败:{}", e.getMessage());
      return false;
    }

    return true;
  }

  /**
   * 将accountCondition对象转化为AccountCheckPrt
   *
   * @param accountCondition
   * @param chkId
   * @return
   */
  private AccountCheckPrt convertAccountConditionToAccountCheckPrt(
      AccountCondition accountCondition, String chkId) {
    AccountCheckPrt accountCheck = new AccountCheckPrt();
    accountCheck.setChkId(chkId);
    accountCheck.setAcgYr(accountCondition.getAcgYr());
    accountCheck.setAcgMo(accountCondition.getAcgMo());
    accountCheck.setCorpCd(accountCondition.getCorpCd());
    accountCheck.setCorpNm(accountCondition.getCorpNm());
    accountCheck.setCstCd(accountCondition.getCstCd());
    accountCheck.setCstNm(accountCondition.getCstNm());
    accountCheck.setSbjCd(accountCondition.getSbjCd());
    String sbjNm = accountCondition.getSbjNm();
    if (sbjNm != null) {
      // 前端传入的是转移后的'\\',需要转化为'\'
      sbjNm = sbjNm.replace("\\\\", "\\");
    }
    accountCheck.setSbjNm(sbjNm);
    accountCheck.setSbjLvl(Integer.valueOf(Integer.parseInt(accountCondition.getSbjLvl())));
    accountCheck.setSbjTpCd(accountCondition.getSbjTpCd());
    accountCheck.setSbjTpNm(accountCondition.getSbjTpNm());
    accountCheck.setAmtEop(new BigDecimal("0"));// 针对手动添加纪录, AMT_EOP总是为零
    accountCheck.setAmtOfst(new BigDecimal(accountCondition.getAmtOfst()));
    accountCheck.setMtchTpNm(accountCondition.getMtchTpNm());
    accountCheck.setMtchRmrk(accountCondition.getRemark());
    accountCheck.setMtchBtchId(accountCondition.getMatchId());
    return accountCheck;
  }

  private Map<String, List<AccountCheckPrt>> generateSeparateLists(
      List<AccountCheckPrt> accountCheckList) {
    Map<String, List<AccountCheckPrt>> selfCntlListMap = new HashMap<>();
    if (accountCheckList != null) {
      List<AccountCheckPrt> selfAccountCheckList = new ArrayList<>();
      List<AccountCheckPrt> cntlAccountCheckList = new ArrayList<>();
      for (AccountCheckPrt account : accountCheckList) {
        if ((KPMG_CHK_ACCOUNT_SELF_TYPE + "").equals(account.getRsltCd())) {
          selfAccountCheckList.add(account);
        } else if ((KPMG_CHK_ACCOUNT_CNTR_TYPE + "").equals(account.getRsltCd())) {
          cntlAccountCheckList.add(account);
        }
      }
      selfCntlListMap.put(KPMG_CHK_ACCOUNT_SELF_TYPE + "", selfAccountCheckList);
      selfCntlListMap.put(KPMG_CHK_ACCOUNT_CNTR_TYPE + "", cntlAccountCheckList);
    }
    return selfCntlListMap;
  }

  /**
   *
   * @param accountCondition 需要传入acg_yr, acg_mo, mtch_btch_id, 不需要传入rslt_cd
   * @param num
   * @param flowNo
   * @return
   */
  private AccountCheckComboVO accountDataComboFetch(AccountCondition accountCondition, int num,
      String flowNo) {

    // 不传入RSLT_CD参数, 根据索引ACG_YR+ACG_MO+MTCH_BTCH_ID找到所有数据, 然后再自己分割
    List<AccountCheckPrt> accountCheckList =
        accountCheckPrtMapper.selectByBatchIdAndRsltCd(accountCondition);

    Map<String, List<AccountCheckPrt>> selfCntlListMap = generateSeparateLists(accountCheckList);
    List<AccountCheckPrt> selfAccountCheckList =
        selfCntlListMap.get(KPMG_CHK_ACCOUNT_SELF_TYPE + "");
    List<AccountCheckPrt> cntlAccountCheckList =
        selfCntlListMap.get(KPMG_CHK_ACCOUNT_CNTR_TYPE + "");

    List<AccountCheckPrt> mnlAccountCheckList =
        accountCheckPrtMapper.selectMnlByBatchId(accountCondition.getMatchId());

    AccountCheckComboVO accountCheckCombo = new AccountCheckComboVO();
    List<AccountCheckRowVO> hostList = new ArrayList<>();
    AccountCheckRowVO accountCheckRow = null;
    String flowName = "分录" + num;
    if (flowNo != null) {
      flowName = flowNo;
    }
    for (AccountCheckPrt accountCheck : selfAccountCheckList) {
      accountCheckCombo.setBatchId(accountCheck.getMtchBtchId());
      accountCheckCombo.setFlowNo(flowName);
      accountCheckCombo.setBookTotal(accountCheck.getAmtEopAcm().toString());
      BigDecimal diffTotal = accountCheck.getAmtEopAcm().subtract(accountCheck.getAmtOfstAcm());
      accountCheckCombo.setDiffTotal(diffTotal.toString());
      accountCheckCombo.setOffsetTotal(accountCheck.getAmtOfstAcm().toString());
      accountCheckCombo.setRemark("");

      accountCheckCombo.setTotalCorpCode(accountCheck.getCorpCd());
      accountCheckCombo.setTotalCstCode(accountCheck.getCstCd());
      accountCheckCombo.setTotalCorpNm(accountCheck.getCorpNm());
      accountCheckCombo.setTotalCstNm(accountCheck.getCstNm());
      String type = accountCheck.getMtchTpNm();
      boolean isManualOper =
          !MATCH_AUTO_EQUAL.equals(type) && !MATCH_SUBJECT_UNDEFINED.equals(type);
      accountCheckCombo.setManualOper(isManualOper);


      accountCheckCombo.setType(type);

      accountCheckRow = new AccountCheckRowVO();
      accountCheckRow.setOwnerName(accountCheck.getCorpNm());
      accountCheckRow.setTradeName(accountCheck.getCstNm());
      accountCheckRow.setCourseType(accountCheck.getSbjNm());
      accountCheckRow.setBookMoney(accountCheck.getAmtEop().toString());
      accountCheckRow.setOffsetMoney(accountCheck.getAmtOfst().toString());
      accountCheckRow.setDiffMoney(accountCheck.getAmtDif().toString());
      accountCheckRow.setOffsetType(accountCheck.getMtchTpNm());
      String remark = accountCheck.getMtchRmrk();
      accountCheckRow.setRemark(remark == null ? "" : remark);
      accountCheckRow.setCorpCode(accountCheck.getCorpCd());
      accountCheckRow.setCstCode(accountCheck.getCstCd());
      String mtchBtchId = accountCheck.getMtchBtchId();
      accountCheckRow.setMtchBtchId(mtchBtchId);
      accountCheckRow.setBookTotal(accountCheck.getAmtEopAcm().toString());
      accountCheckRow.setOffsetTotal(accountCheck.getAmtOfstAcm().toString());
      accountCheckRow.setDiffTotal(diffTotal.toString());
      accountCheckRow.setDeleteFlag(false);
      hostList.add(accountCheckRow);
    }
    accountCheckCombo.setHostList(hostList);

    // 对方数据, 根据MTCH_BTCH_ID和RSLT_CD=1从KPMG_CHKACT_PRT表中获取
    List<AccountCheckRowVO> tradeList = new ArrayList<>();
    for (AccountCheckPrt accountCheck : cntlAccountCheckList) {
      // 对方数据是不可以被删除的, 人为设置deleteFlag
      accountCheckRow = new AccountCheckRowVO();
      accountCheckRow.setOwnerName(accountCheck.getCorpNm());
      accountCheckRow.setTradeName(accountCheck.getCstNm());
      accountCheckRow.setCourseType(accountCheck.getSbjNm());
      accountCheckRow.setBookMoney(accountCheck.getAmtEop().toString());
      accountCheckRow.setOffsetMoney(accountCheck.getAmtOfst().toString());
      accountCheckRow.setDiffMoney(accountCheck.getAmtDif().toString());
      accountCheckRow.setOffsetType(accountCheck.getMtchTpNm());
      String remark = accountCheck.getMtchRmrk();
      accountCheckRow.setRemark(remark == null ? "" : remark);
      accountCheckRow.setCorpCode(accountCheck.getCorpCd());
      accountCheckRow.setCstCode(accountCheck.getCstCd());
      String mtchBtchId = accountCheck.getMtchBtchId();
      accountCheckRow.setMtchBtchId(mtchBtchId);
      accountCheckRow.setBookTotal(accountCheck.getAmtEopAcm().toString());
      accountCheckRow.setOffsetTotal(accountCheck.getAmtOfstAcm().toString());
      BigDecimal diffTotal = accountCheck.getAmtEopAcm().subtract(accountCheck.getAmtOfstAcm());
      accountCheckRow.setDiffTotal(diffTotal.toString());
      accountCheckRow.setDeleteFlag(false);
      tradeList.add(accountCheckRow);
    }

    for (AccountCheckPrt accountCheck : mnlAccountCheckList) {
      // 手动录入数据是可以被删除的, 人为设置deleteFlag为true
      accountCheckRow = new AccountCheckRowVO();
      accountCheckRow.setChkId(accountCheck.getChkId());
      accountCheckRow.setOwnerName(accountCheck.getCorpNm());
      accountCheckRow.setTradeName(accountCheck.getCstNm());
      accountCheckRow.setCourseType(accountCheck.getSbjNm());
      accountCheckRow.setBookMoney(accountCheck.getAmtEop().toString());
      accountCheckRow.setOffsetMoney(accountCheck.getAmtOfst().toString());
      BigDecimal amtDif = accountCheck.getAmtDif();
      accountCheckRow.setDiffMoney(amtDif != null ? amtDif.toString() : "");
      accountCheckRow.setOffsetType(accountCheck.getMtchTpNm());
      String remark = accountCheck.getMtchRmrk();
      accountCheckRow.setRemark(remark);
      accountCheckRow.setCorpCode(accountCheck.getCorpCd());
      accountCheckRow.setCstCode(accountCheck.getCstCd());
      accountCheckRow.setDeleteFlag(true);
      tradeList.add(accountCheckRow);
    }
    accountCheckCombo.setTradeList(tradeList);
    int accountSize = hostList.size() + tradeList.size();
    boolean isDisplayTotal = MATCH_UNILATERAL_ACCOUNT.equals(accountCheckCombo.getType())
        && !CollectionUtils.isEmpty(tradeList)
        || !MATCH_UNILATERAL_ACCOUNT.equals(accountCheckCombo.getType())
        && !MATCH_SUBJECT_UNDEFINED.equals(accountCheckCombo.getType());
    accountCheckCombo.setDisplayTotal(isDisplayTotal);

    accountCheckCombo.setAccountSize(accountSize);

    return accountCheckCombo;

  }

  @Override
  @DynamicDS("projectId")
  public synchronized ServerResponse<JSONObject> deleteManualRow(AccountRowDeleteVO accountRowDeleteVO,
      String projectId) {
    String chkId = accountRowDeleteVO.getMatchId();
    String accountName = accountRowDeleteVO.getAccountName();
    String acgYr = accountRowDeleteVO.getAcgYr();
    String acgMo = accountRowDeleteVO.getAcgMo();
    AccountCheckPrt selfAccountCheck = accountCheckPrtMapper.selectAccountCheckMnlByChkId(chkId);
    if (selfAccountCheck == null) {
      return ServerResponse.createByErrorMessage("删除未找到对应数据:" + chkId);
    }
    String batchId = selfAccountCheck.getMtchBtchId();

    boolean isDeleteMnlSucc =
        this.processAccountCheckMnlDeleteAdjustment(chkId, selfAccountCheck, acgYr, acgMo);
    if (!isDeleteMnlSucc) {
      return ServerResponse.createByErrorMessage("删除手工记录失败:" + chkId);
    }

    List<AccountCheckComboVO> reportData = new ArrayList<>();

    AccountCondition accountCondition = new AccountCondition();
    accountCondition.setAcgYr(acgYr);
    accountCondition.setAcgMo(acgMo);
    accountCondition.setMatchId(batchId);

    AccountCheckComboVO accountDetailDataMap =
        accountDataComboFetch(accountCondition, 0, accountName);
    reportData.add(accountDetailDataMap);

    JSONObject result = new JSONObject();
    result.put("list", reportData);
    result.put(ACCOUNT_SIZE, accountDetailDataMap.getAccountSize());

    return ServerResponse.createBySuccess("删除手工记录成功", result);

  }

  @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000,
      rollbackFor = Exception.class)
  public boolean processAccountCheckMnlDeleteAdjustment(String chkId,
      AccountCheckPrt selfAccountCheck, String acgYr, String acgMo) {

    boolean processSucc = true;

    String batchId = selfAccountCheck.getMtchBtchId();
    BigDecimal amtOfst = selfAccountCheck.getAmtOfst();

    try {
      // 更新CHK_ACT_PRT_TABLE-对账结果表（对方记录）, AMT_EOP_ACM = AMT_EOP_ACM +
      // 要删除手工记录的AMT_OFST, AMT_OFST_ACM = AMT_OFST_ACM + 要删除手工记录的AMT_OFST,
      // 根据MTCH_BTCH_ID AND RSTL_CD = 1(注:条件这里将来会有变动)
      AccountCheckPrt cntlAccountChkPrt = new AccountCheckPrt();
      cntlAccountChkPrt.setAcgYr(acgYr);
      cntlAccountChkPrt.setAcgMo(acgMo);
      cntlAccountChkPrt.setMtchBtchId(batchId);
      cntlAccountChkPrt.setRsltCd(KPMG_CHK_ACCOUNT_CNTR_TYPE + "");
      cntlAccountChkPrt.setAmtOfst(amtOfst);
      int updateCntlDataNum =
          accountCheckPrtMapper.updateCntlPrtAmountByBtchRslt(cntlAccountChkPrt);

      AccountCheckPrt accountCheckMnl = new AccountCheckPrt();
      accountCheckMnl.setChkId(chkId);
      int deleteMnlDataNum = accountCheckPrtMapper.deleteAccountCheckMnl(accountCheckMnl);

      // 更新CHK_ACT_PRT_TABLE-对账结果表（本方记录）,MTCH_TP_NM状态置为初始状态MTCH_TP_NM_INIT(请看下一行解释), AMT_DIF =
      // AMT_DIF +
      // 要删除手工记录的AMT_DIF, AMT_OFST = AMT_OFST - 要删除手工记录的AMT_DIF, 根据MTCH_BTCH_ID AND
      // RSTL_CD = 0(注:条件这里将来会有变动)
      // 注: 如果原始状态为'单边记账'且在删除手动记录后没有其他手动记录时, 更新状态为MTCH_TP_NM_INIT(即4), 不然都更新为'抵小抵消'(即3)
      AccountCheckPrt selfAccountChkPrt = new AccountCheckPrt();
      selfAccountChkPrt.setAcgYr(acgYr);
      selfAccountChkPrt.setAcgMo(acgMo);
      selfAccountChkPrt.setMtchBtchId(batchId);
      selfAccountChkPrt.setRsltCd(KPMG_CHK_ACCOUNT_SELF_TYPE + "");
      selfAccountChkPrt.setMtchTpNm(MATCH_AUTO_OFFSET);
      selfAccountChkPrt.setAmtOfst(amtOfst);
      int updateSelfSigle =
          accountCheckPrtMapper.updateSelfPrtAmountByBtchRsltSigle(selfAccountChkPrt);

      if (logger.isDebugEnabled()) {
        logger.debug("Update {} data for Cntl AccountCheckSigle", updateSelfSigle);
        logger.debug("Update {} data for Cntl AccountCheckPrt", updateCntlDataNum);
        logger.debug("delete {} data for Mnl AccountCheckPrt", deleteMnlDataNum);
      }
    } catch (Exception e) {
      logger.error("删除手动记录异常:{}", e.getMessage());
      processSucc = false;
    }

    return processSucc;
  }

  private String getUUID() {
    return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
  }

  @Override
  @DynamicDS("projectId")
  public ServerResponse getCstMapping(String projectId, String param, int page, int size) {
    PageHelper.startPage(page, size);
    List<AccountCheckCstMapping> result = null;
    try {
      result = accountCheckCstMappingMapper.getCstMapping(param);
      PageInfo pageData = new PageInfo<>(result);
      JSONObject resultObj = new JSONObject();
      // 是否存在下一页
      resultObj.put(HAS_NEXT_PAGE, pageData.isHasNextPage());
      resultObj.put("list", pageData.getList());
      resultObj.put("total",pageData.getTotal());
      return ServerResponse.createBySuccess("获取交易对手列表成功", resultObj);
    } catch (Exception e) {
      logger.error("获取交易对手列表失败:{}", e.getMessage());
      JSONObject resultObj = new JSONObject();
      // 是否存在下一页
      resultObj.put(HAS_NEXT_PAGE, false);
      resultObj.put("list", new ArrayList<>());
      return ServerResponse.createBySuccess("获取交易对手列表失败, 结果为空", resultObj);
    }
  }

  @Override
  @DynamicDS("projectId")
  public ServerResponse updateCstMapping(String projectId, String code, String value, String corpCd,
      String corpNm) {
    try {
      AccountCheckCstMapping cstMapping = new AccountCheckCstMapping();
      cstMapping.setCode(code);
      cstMapping.setValue(value);
      cstMapping.setCorpCd(corpCd);
      cstMapping.setCorpNm(corpNm);
      accountCheckCstMappingMapper.updateCstMapping(cstMapping);
      return ServerResponse.createBySuccessMessage("更新交易对手标准名称成功");
    } catch (Exception e) {
      logger.error("更新交易对手标准名称失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("更新交易对手标准名称失败");
    }
  }

  // 获取科目匹配查询Mapping
  @DynamicDS("projectId")
  public ServerResponse getSubMatchMapping(String projectId, String param, int page, int size) {
    PageHelper.startPage(page, size);
    List<String> queryMatchIds = null;
    // 模糊查询，根据输入的科目编码查询科目明细得到matchId
    if (param != null && param != "") {
      List<AccountCheckSubDetailMapping> detailMappers =
          accountCheckSubMapper.getSubMatchMapping(null, param);
      queryMatchIds = new ArrayList<>();
      if (CollectionUtils.isEmpty(detailMappers)) {
        return ServerResponse.createBySuccessMessage("未查询到匹配规则列表");
      }

      for (AccountCheckSubDetailMapping detailMapper : detailMappers) {
        queryMatchIds.add(detailMapper.getMatchId());
      }

    }
    List<AccountCheckSubDetailMapping> result = null;
    List<AccountCheckSubVO> finalResult = new ArrayList<>();
    AccountCheckSubVO accountCheckSubVO = null;
    try {
      // 若明细matchid为空，查询所有科目匹配规则表
      List<AccountCheckSubMapping> matchMappers =
          accountCheckSubMapper.getSubMatchList(queryMatchIds);
      for (AccountCheckSubMapping matchMapper : matchMappers) {
        List<AccountCheckSubQueryVO> sbjList1 = new ArrayList<>();
        List<AccountCheckSubQueryVO> sbjList0 = new ArrayList<>();
        // 根据规则表的matchid得到对应明细
        result = accountCheckSubMapper.getSubMatchMapping(matchMapper.getMatchId(), null);
        // 封装vo
        accountCheckSubVO = new AccountCheckSubVO();
        accountCheckSubVO.setMatchId(matchMapper.getMatchId());
        accountCheckSubVO.setMatchName(matchMapper.getSubMap());
        accountCheckSubVO.setMatchType(matchMapper.getMatchType());
        accountCheckSubVO.setPriority(matchMapper.getPriority());
        if (result != null && !result.isEmpty()) {
          getSubDetail(result, sbjList1, sbjList0);

        }
        accountCheckSubVO.setOtherSbjList(sbjList1);
        accountCheckSubVO.setOwnerSbjList(sbjList0);
        finalResult.add(accountCheckSubVO);
      }

      PageInfo pageData = new PageInfo<>(finalResult);

      return ServerResponse.createBySuccess("获取匹配规则列表成功", pageData);
    } catch (Exception e) {
      logger.error("获取匹配规则列表失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("获取匹配规则列表失败");
    }
  }

  // 科目匹配--本分对方明细
  public void getSubDetail(List<AccountCheckSubDetailMapping> result,
      List<AccountCheckSubQueryVO> sbjList1, List<AccountCheckSubQueryVO> sbjList0) {
    for (AccountCheckSubDetailMapping accountCheckSubDetailMapping : result) {
      AccountCheckSubQueryVO accountCheckSubQueryVO = new AccountCheckSubQueryVO();
      accountCheckSubQueryVO.setCode(accountCheckSubDetailMapping.getSubCd());
      accountCheckSubQueryVO.setValue(accountCheckSubDetailMapping.getSubNm());
      accountCheckSubQueryVO.setSubType(accountCheckSubDetailMapping.getSubType());
      if (accountCheckSubDetailMapping.getSubRslType() == 1) {
        sbjList1.add(accountCheckSubQueryVO);

      } else {
        sbjList0.add(accountCheckSubQueryVO);
      }
    }
  }

  // 获取科目匹配查询Mapping 返回
  public List<AccountCheckSubVO> getMappingDetail(String param, int page, int size) {
    int start = (page - 1) * size + 1;
    int end = page * size;
    List<AccountCheckSubVO> finalResult = new ArrayList<>();
    List<AccountCheckSubPrt> detailMappers = null;
    // param模糊查询--输入科目编码，查询出明细
    if (param != null && param != "") {
      detailMappers = accountCheckSubPrtMapper.getSubMatchAndDetail(start, end, param);
    } else {
      detailMappers = accountCheckSubPrtMapper.getSubMatchAndDetail(start, end, null);
    }
    Map<String, List<AccountCheckSubPrt>> matchIdAll = new LinkedHashMap<>();
    List<AccountCheckSubPrt> listsMatch = null;
    AccountCheckSubVO accountCheckSubVO = null;
    // 获取科目匹配表中的matchID，保存所有id
    for (AccountCheckSubPrt accountHeaderAndDetail : detailMappers) {
      String matchId = accountHeaderAndDetail.getMatchId();
      if (matchIdAll.containsKey(matchId)) {
        listsMatch = matchIdAll.get(matchId);
      } else {
        listsMatch = new ArrayList<>();
      }
      listsMatch.add(accountHeaderAndDetail);
      matchIdAll.put(matchId, listsMatch);
    }
    Iterator<Map.Entry<String, List<AccountCheckSubPrt>>> it = matchIdAll.entrySet().iterator();
    // 根据matchId将科目匹配明细分到本分对方list
    while (it.hasNext()) {
      Map.Entry<String, List<AccountCheckSubPrt>> matchDetail = it.next();
      accountCheckSubVO = new AccountCheckSubVO();
      accountCheckSubVO.setMatchId(matchDetail.getValue().get(0).getMatchId());
      accountCheckSubVO.setMatchName(matchDetail.getValue().get(0).getSubMap());
      accountCheckSubVO.setMatchType(matchDetail.getValue().get(0).getMatchType());
      accountCheckSubVO.setPriority(matchDetail.getValue().get(0).getPriority());
      List<AccountCheckSubQueryVO> otherSbjLists = new ArrayList<>();
      List<AccountCheckSubQueryVO> ownerSbjLists = new ArrayList<>();
      for (AccountCheckSubPrt subs : matchDetail.getValue()) {
        AccountCheckSubQueryVO accountCheckSubQueryVO = new AccountCheckSubQueryVO();
        accountCheckSubQueryVO.setCode(subs.getSubCd());
        accountCheckSubQueryVO.setValue(subs.getSubNm());
        accountCheckSubQueryVO.setSubType(subs.getSubType());
        if (subs.getSubRslType() == 1) {
          otherSbjLists.add(accountCheckSubQueryVO);

        } else {
          ownerSbjLists.add(accountCheckSubQueryVO);
        }
      }
      accountCheckSubVO.setOtherSbjList(otherSbjLists);
      accountCheckSubVO.setOwnerSbjList(ownerSbjLists);
      finalResult.add(accountCheckSubVO);

    }
    return finalResult;
  }

  // 获取科目匹配查询Mapping --改
  @Override
  @DynamicDS("projectId")
  public ServerResponse getSubMatchAndDetailMapping(String projectId, String param, int page,
      int size) {
    try {
      List<AccountCheckSubVO> finalResult = this.getMappingDetail(param, page, size);
      PageInfo pageData = new PageInfo<>(finalResult);
      return ServerResponse.createBySuccess("获取匹配规则列表成功", pageData);
    } catch (Exception e) {
      logger.error("获取匹配规则列表失败:{}", e.getMessage());
      PageInfo pageData = new PageInfo<>(new ArrayList<>());
      return ServerResponse.createBySuccess("获取匹配规则列表失败, 结果为空", pageData);
    }
  }



  /**
   * 获取科目类型列表--改
   *
   * @return
   */
  @Override
  @DynamicDS("projectId")
  public ServerResponse getCourseByType(int page, int size, String projectId, List<String> types,
      String code, String value, String param) {

    PageHelper.startPage(page, size);
    try {
      if (StringUtils.isEmpty(code)) {
        code = null;
      }
      if (StringUtils.isEmpty(value)) {
        value = null;
      }
      List<AccountCheckSubRow> result =
          accountCheckSubMapper.queryCouseByType(types, code, value, param);
      PageInfo pageData = new PageInfo<>(result);
      JSONObject json = new JSONObject();

      json.put(HAS_NEXT_PAGE, pageData.isHasNextPage());
      json.put("list", pageData.getList());

      return ServerResponse.createBySuccess("获取科目列表成功", json);
    } catch (Exception e) {
      logger.error("获取科目列表失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("获取科目列表失败");
    }
  }

  /**
   * 新增规则(科目匹配 科目明细)
   *
   * @return
   */
  @Override
  @DynamicDS("projectId")
  public ServerResponse addRuleRow(AccountSubCondition accountSubCondition, String projectId) {
    try {
      String uuid = this.getUUID();
      this.addRuleRowAndDetail(accountSubCondition, uuid);
      JSONObject resultObj = new JSONObject();
      resultObj.put("matchId", uuid);
      return ServerResponse.createBySuccess("新增匹配规则成功", resultObj);
    } catch (Exception e) {
      logger.error("新增匹配规则失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("新增匹配规则失败");
    }
  }

  // 添加科目规则
  public void addRuleRowAndDetail(AccountSubCondition accountSubCondition, String uuid) {
    String prioprity = accountCheckSubMapper.selectPrioprity();
    AccountCheckSubMapping accountSubHeader = new AccountCheckSubMapping();
    accountSubHeader.setMatchId(uuid);
    accountSubHeader.setSubMap(accountSubCondition.getSubMappingName());
    accountSubHeader.setMatchType(accountSubCondition.getSubMappingType());
    accountSubHeader.setMatchDesc(accountSubCondition.getSubDesc());
    accountSubHeader.setPriority(prioprity);
    List<AccountCheckSubQueryVO> ownerLists = accountSubCondition.getOwnerSbjList();
    List<AccountCheckSubQueryVO> otherLists = accountSubCondition.getOtherSbjList();
    List<AccountCheckSubDetailMapping> accountSubDetailList = new ArrayList<>();
    // 获取本分对方明细
    this.getAccountSubDetail(ownerLists, uuid, accountSubDetailList, 0);
    this.getAccountSubDetail(otherLists, uuid, accountSubDetailList, 1);
    int insertSubHeader = accountCheckSubMapper.insertAccountSubHeader(accountSubHeader);
    int insertSubDetail = accountCheckSubMapper.insertBatchSubDetail(accountSubDetailList);

    if (logger.isDebugEnabled()) {
      logger.debug("insert{} data for KPMG_SBJ_MAPPING_HEADER", insertSubHeader);
      logger.debug("insert {} data for KPMG_SBJ_MAPPING_DETAIL", insertSubDetail);
    }
  }

  /**
   * 删除科目匹配
   *
   * @return
   */
  @Override
  @DynamicDS("projectId")
  public ServerResponse deleteRuleRow(String matchId, String projectId) {
    try {
      AccountCheckSubMapping accountCheckSubMapping =
          accountCheckSubMapper.getSubMatchById(matchId);
      if (accountCheckSubMapping == null) {
        return ServerResponse.createByErrorMessage("删除KPMG_SBJ_MAPPING_HEADER未找到对应数据:" + matchId);
      }
      int deleteRowById = accountCheckSubMapper.deleteRuleRow(accountCheckSubMapping);
      this.deleteRuleDetailRow(matchId);
      if (logger.isDebugEnabled()) {
        logger.debug("delete{} data for KPMG_SBJ_MAPPING_HEADER", deleteRowById);
      }
      return ServerResponse.createBySuccessMessage("删除记录成功");

    } catch (Exception e) {
      logger.error("删除记录失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("删除记录失败");
    }
  }

  /**
   * 删除科目匹配明细
   *
   * @return
   */
  public ServerResponse deleteRuleDetailRow(String matchId) {
    try {
      int count = accountCheckSubMapper.getSubMatchDetailById(matchId);
      if (count == 0) {
        return ServerResponse.createByErrorMessage("删除KPMG_SBJ_MAPPING_DETAIL未找到对应数据:" + matchId);
      }
      int deleteRowDetailById = accountCheckSubMapper.deleteRuleDetailRow(matchId);
      if (logger.isDebugEnabled()) {
        logger.debug("delete {} data for KPMG_SBJ_MAPPING_DETAIL", deleteRowDetailById);
      }
      return ServerResponse.createBySuccessMessage("删除明细记录成功");

    } catch (Exception e) {
      logger.error("删除明细记录失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("删除明细记录失败");
    }
  }

  /**
   * 更新科目匹配
   *
   * @return
   */
  public int updateRuleRow(AccountSubCondition accountSubCondition) {
    AccountCheckSubMapping accountCheckSubMapping = new AccountCheckSubMapping();
    accountCheckSubMapping.setMatchId(accountSubCondition.getMatchId());
    if (!StringUtils.isEmpty(accountSubCondition.getSubMappingName())) {
      accountCheckSubMapping.setSubMap(accountSubCondition.getSubMappingName());
    }
    if (!StringUtils.isEmpty(accountSubCondition.getPriority())) {
      accountCheckSubMapping.setPriority(accountSubCondition.getPriority());
    }
    if (!StringUtils.isEmpty(accountSubCondition.getSubMappingType())) {
      accountCheckSubMapping.setMatchType(accountSubCondition.getSubMappingType());
    }
    return accountCheckSubMapper.updateSubHeader(accountCheckSubMapping);
  }

  /**
   * 更新匹配科目及明细
   *
   * @return
   */
  @Override
  @DynamicDS("projectId")
  public ServerResponse updateRuleAllRow(AccountSubCondition accountSubCondition,
      String projectId) {
    try {
      if (StringUtils.isEmpty(accountSubCondition.getMatchId())) {
        return ServerResponse.createByErrorMessage("更新未找到对应数据:");
      }
      if (!(StringUtils.isEmpty(accountSubCondition.getSubMappingName())
          && StringUtils.isEmpty(accountSubCondition.getSubMappingType())
          && StringUtils.isEmpty(accountSubCondition.getPriority()))) {
        // 更新科目匹配
        int updateSubHeader = this.updateRuleRow(accountSubCondition);
        if (logger.isDebugEnabled()) {
          logger.debug("update {} data for KPMG_SBJ_MAPPING_HEADER", updateSubHeader);
        }
      }
      List<AccountCheckSubQueryVO> ownerLists = accountSubCondition.getOwnerSbjList();
      List<AccountCheckSubQueryVO> otherLists = accountSubCondition.getOtherSbjList();
      List<AccountCheckSubDetailMapping> accountSubDetailList = new ArrayList<>();
      // 获取本分对方明细
      this.getAccountSubDetail(ownerLists, accountSubCondition.getMatchId(), accountSubDetailList,
          0);
      this.getAccountSubDetail(otherLists, accountSubCondition.getMatchId(), accountSubDetailList,
          1);
      this.deleteRuleDetailRow(accountSubCondition.getMatchId());
      // 更新科目匹配明细
      int updateSubDetail = accountCheckSubMapper.insertBatchSubDetail(accountSubDetailList);
      if (logger.isDebugEnabled()) {
        logger.debug("update {} data for KPMG_SBJ_MAPPING_DETAIL", updateSubDetail);
      }
      return ServerResponse.createBySuccessMessage("更新记录成功");

    } catch (Exception e) {
      logger.error("更新记录失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("更新记录失败");
    }
  }

  @Override
  @DynamicDS("projectId")
  public ServerResponse moveAccountSubHeader(AccountCheckChangeRowVO matchIds, String projectId) {
    try {

      int moveProority = accountCheckSubMapper.changeSubHeaderRow(matchIds.getMatchIdOne(),
          matchIds.getMatchIdTwo());
      if (logger.isDebugEnabled()) {
        logger.debug("moveProority {} data for KPMG_SBJ_MAPPING_HEADER", moveProority);
      }
      return ServerResponse.createBySuccessMessage("移动优先级记录成功");

    } catch (Exception e) {
      logger.error("移动优先级记录失败:{}", e.getMessage());
      return ServerResponse.createByErrorMessage("移动优先级记录失败");
    }
  }

  public List<AccountCheckSubDetailMapping> getAccountSubDetail(List<AccountCheckSubQueryVO> lists,
      String uuid, List<AccountCheckSubDetailMapping> accountSubDetailList, int rstlCd) {
    for (AccountCheckSubQueryVO list : lists) {
      AccountCheckSubDetailMapping accountSubDetail = new AccountCheckSubDetailMapping();
      accountSubDetail.setMatchId(uuid);
      accountSubDetail.setSubRslType(rstlCd);
      accountSubDetail.setSubCd(list.getCode());
      accountSubDetail.setSubNm(list.getValue());
      accountSubDetail.setSubType(list.getSubType());
      accountSubDetailList.add(accountSubDetail);
    }
    return accountSubDetailList;
  }

  /**
   * 内部对账整体执行
   *
   * @param projectId
   * @param year
   * @param startMonth
   * @param endMonth
   */
  @Override
  @DynamicDS("projectId")
  public void executeAccountCheckHandler(String projectId, String year, int startMonth,
      int endMonth) {

    Map<String, List<Set<String>>> bdRuleSbjListMap =
        ((AccountCheckServiceImpl) AopContext.currentProxy()).queryAccountMatchRules(projectId);
//    Map<String, List<Set<String>>> zfRuleSbjListMap =
//        ((AccountCheckServiceImpl) AopContext.currentProxy()).queryAccountMatchRules(projectId,
//            SBJ_MAPPING_TYPE_ZF);
//    Map<String, List<Set<String>>> scRuleSbjListMap =
//        ((AccountCheckServiceImpl) AopContext.currentProxy()).queryAccountMatchRules(projectId,
//            SBJ_MAPPING_TYPE_SC);

    String bdType = "本方对对方";
//    String sCType = "收入对成本和费用";

    // 按月份进行内部对账
    String compareDate = "";
    AccountCheckExecuteResultDTO zfResultDto = null;
    Map<String, List<AccountOrigin>> zfTradeList = null;
    Map<String, List<AccountOrigin>> scfHostList = null;
    Map<String, List<AccountOrigin>> scfTradeList = null;
    AccountCheckExecuteResultDTO sCResultDto = null;

    Map<String, List<AccountOrigin>> zfHostList = null;
    for (int month = startMonth; month <= endMonth; month++) {
      String item = (month < 10 ? "0" : "") + month;
      // 时间点: 年月
      compareDate = year + item;

      // 核对是否是增量更新，还是全量更新对账结果表中的数据
      boolean isDataClearSuccess = ((AccountCheckServiceImpl) AopContext.currentProxy())
          .clearAccountCheckData(projectId, year, item);

      if (!isDataClearSuccess) {
        logger.error("清除原有数据异常, 年:{}, 月份:{}, 退出", year, item);
        break;
      }

      long startTime = new Date().getTime();
      // 本方(0) -> 对方(1)
      zfHostList = ((AccountCheckServiceImpl) AopContext.currentProxy())
          .getOriginalCompareDataListMap(projectId, year, item, "01,02,03,04,05", SBJ_RSLT_CD_SELF);
      zfTradeList = ((AccountCheckServiceImpl) AopContext.currentProxy())
          .getOriginalCompareDataListMap(projectId, year, item, "01,02,03,04,05", SBJ_RSLT_CD_CNTL);
      zfResultDto = this.processAccountCheckData(zfHostList, zfTradeList, bdRuleSbjListMap);
      this.processResultToDB(projectId, bdType, zfResultDto);

      // 收入(03) -> 成本(04) + 费用(05)
//      scfHostList = ((AccountCheckServiceImpl) AopContext.currentProxy())
//          .getOriginalCompareDataListMap(projectId, year, item, "03", SBJ_RSLT_CD_SELF);
//      scfTradeList = ((AccountCheckServiceImpl) AopContext.currentProxy())
//          .getOriginalCompareDataListMap(projectId, year, item, "04, 05", SBJ_RSLT_CD_CNTL);
//      sCResultDto = this.processAccountCheckData(scfHostList, scfTradeList, scRuleSbjListMap);
//      this.processResultToDB(projectId, sCType, sCResultDto);

      long endTime = new Date().getTime();
      long costTime = endTime - startTime;

      logger.info("日期区间为 {} 的数据对账完毕, 耗时：{}秒", compareDate, costTime / 1000);

    }

    logger.info("内部对账{}", "执行结束！");

  }

  /**
   * 清空结果表中的数据, 同时更新目标表中所有数据的状态到未匹配
   *
   * @param projectId
   * @param acgYr
   * @param acgMo
   * @return
   */
  @DynamicDS("projectId")
  @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000,
      rollbackFor = Exception.class)
  public boolean clearAccountCheckData(String projectId, String acgYr, String acgMo) {
    logger.info("清空内部对账结果表 项目: {}, 年份: {}, 月份: {}", projectId, acgYr, acgMo);
    AccountCheckPrt condition = new AccountCheckPrt();
    condition.setAcgMo(acgMo);
    condition.setAcgYr(acgYr);
    try {
      int deleteMnlNumber = accountCheckPrtMapper.deleteAccountCheckMnl(condition);
      int deletePrtNumber = accountCheckPrtMapper.deleteAccountCheckPrt(condition);
      int updateOriginalDataStatusNumber = accountCheckHandlerMapper.resetDataStatus(acgYr, acgMo);
      if (logger.isDebugEnabled()) {
        logger.debug("Delete {} data for AccountCheckPrt", deletePrtNumber);
        logger.debug("Delete {} data for Mnl AccountCheckPrt", deleteMnlNumber);
        logger.debug("Update {} data Status for KPMG_CHKACT_SRCDTL",
            updateOriginalDataStatusNumber);
      }
    } catch (Exception e) {
      logger.error("删除内部对账数据异常: " + e.getMessage(), e);
      return false;
    }
    return true;
  }



  /**
   * 从KPMG_SBJ_MAPPING_HEADER表中获取MappingId->priority的Map
   *
   * @return Map<String, Integer>
   */
  private Map<String, Integer> getMappingPriorityMap() {
    Map<String, Integer> mappingIdPriorityMap = null;
    try {
      List<AccountCheckSubMapping> mappingList = accountCheckSubMapper.getSubMatchList(null);
      if (!CollectionUtils.isEmpty(mappingList)) {
        mappingIdPriorityMap = new LinkedHashMap<>();
        for (AccountCheckSubMapping mappingRule : mappingList) {
          mappingIdPriorityMap.put(mappingRule.getMatchId(),
              new Integer(mappingRule.getPriority()));
        }
      }
    } catch (Exception e) {
      logger.error("内部对账查找匹配规则异常" + e.getMessage(), e);
    }
    return mappingIdPriorityMap;
  }

  /**
   * 获取内部对账匹配规则 构建三个Map, 第一个存放ID与本方对方匹配规则映射, 第二个存放本方每个条件和ID映射, 第三个存放对方每个条件和ID映射
   * C04使用
   * @param projectId 项目Id，用于切换数据源
   * @return Map<String, List<Set<String>>>
   */
  @DynamicDS("projectId")
  public Map<String, List<Set<String>>> queryAccountMatchRules(String projectId) {

    List<AccountCheckSubDetailMapping> mappingSbjList =
        accountCheckSubMapper.getMappingSubDetailListByType(null);

    Map<String, Integer> mappingPriorityMap = this.getMappingPriorityMap();
    Map<String, List<Set<String>>> tempMap = new HashMap<>();

    if (!CollectionUtils.isEmpty(mappingSbjList)) {
      for (AccountCheckSubDetailMapping sbjDetail : mappingSbjList) {
        String sbjMappingId = sbjDetail.getMatchId();
        String sbjCd = sbjDetail.getSubCd();
        String sbjNm = sbjDetail.getSubNm();
        String subject = sbjCd + UNDERLINE_SEPARATOR + sbjNm;
        if (!tempMap.containsKey(sbjMappingId)) {
          List<Set<String>> list = new ArrayList<>();
          list.add(new HashSet<>());
          list.add(new HashSet<>());
          tempMap.put(sbjMappingId, list);
        }
        List<Set<String>> resultList = tempMap.get(sbjMappingId);
        // sbjDetail.getSubRslType() 0代表本方 1代表对方
        resultList.get(sbjDetail.getSubRslType()).add(subject);

      }
    }

    Map<String, List<Set<String>>> mappingSubSetListMap = new LinkedHashMap<>();
    if (mappingPriorityMap != null) {
      for (Map.Entry<String, Integer> entry : mappingPriorityMap.entrySet()) {
        String key = entry.getKey();
        if (tempMap.containsKey(key)) {
          mappingSubSetListMap.put(key, tempMap.get(key));
        }
      }
    }

    return mappingSubSetListMap;

  }

  /**
   * 获取内部对账匹配规则 构建三个Map, 第一个存放ID与本方对方匹配规则映射, 第二个存放本方每个条件和ID映射, 第三个存放对方每个条件和ID映射
   * C03使用
   * @param projectId 项目Id，用于切换数据源
   * @param sbjMappingType 规则类型
   * @return Map<String, List<Set<String>>>
   */
  @DynamicDS("projectId")
  public Map<String, List<Set<String>>> queryAccountMatchRules_Tim_C03(String projectId,
      String sbjMappingType) {

    List<AccountCheckSubDetailMapping> mappingSbjList =
        accountCheckSubMapper.getMappingSubDetailListByType(sbjMappingType);

    Map<String, Integer> mappingPriorityMap = this.getMappingPriorityMap();
    Map<String, List<Set<String>>> tempMap = new HashMap<>();

    if (!CollectionUtils.isEmpty(mappingSbjList)) {
      for (AccountCheckSubDetailMapping sbjDetail : mappingSbjList) {
        String sbjMappingId = sbjDetail.getMatchId();
        String sbjCd = sbjDetail.getSubCd();
        String sbjNm = sbjDetail.getSubNm();
        String subject = sbjCd + UNDERLINE_SEPARATOR + sbjNm;
        if (!tempMap.containsKey(sbjMappingId)) {
          List<Set<String>> list = new ArrayList<>();
          list.add(new HashSet<>());
          list.add(new HashSet<>());
          tempMap.put(sbjMappingId, list);
        }
        List<Set<String>> resultList = tempMap.get(sbjMappingId);
        // sbjDetail.getSubRslType() 0代表本方 1代表对方
        if (SBJ_MAPPING_TYPE_ZF.equals(sbjMappingType)) {
          // 颠倒本方，对方位置
          resultList.get((sbjDetail.getSubRslType() == 0) ? 1 : 0).add(subject);
        } else {
          resultList.get(sbjDetail.getSubRslType()).add(subject);
        }

      }
    }

    Map<String, List<Set<String>>> mappingSubSetListMap = new LinkedHashMap<>();
    if (mappingPriorityMap != null) {
      for (Map.Entry<String, Integer> entry : mappingPriorityMap.entrySet()) {
        String key = entry.getKey();
        if (tempMap.containsKey(key)) {
          mappingSubSetListMap.put(key, tempMap.get(key));
        }
      }
    }

    return mappingSubSetListMap;

  }

  /**
   * 获取比较对象集合 如果是本方, 那么使用corpNm_cstStandardNm作为Key 如果是对方, 那么使用cstStandardNm_cstNm作为Key 这样针对每个key,
   * 获取一个结果集, 作为内不匹配对账的原始数据
   *
   * @param year
   * @param month
   * @param type
   * @param selfCntlFlag
   * @return
   */
  @DynamicDS("projectId")
  public Map<String, List<AccountOrigin>> getOriginalCompareDataListMap(String projectId,
      String year, String month, String type, int selfCntlFlag) {

    List<AccountOrigin> resultList =
        accountCheckHandlerMapper.selectCompareDataListNew(year, month, type, selfCntlFlag);

    boolean flag = false;

    if (type.equals("01") || type.equals("02")) {
      flag = true;
    }
    return groupTargetResultMap(resultList, selfCntlFlag, flag);

  }

  /**
   * 将数据List集合转化为需要的Map形式
   *
   * @param resultList
   * @param selfCntlFlag
   * @return
   */
  public Map<String, List<AccountOrigin>> groupTargetResultMap(List<AccountOrigin> resultList,
      int selfCntlFlag, boolean typeFlag) {
    Map<String, List<AccountOrigin>> resultMap = new HashMap<>();
    if (!CollectionUtils.isEmpty(resultList)) {
      resultList.forEach(record -> {
        String key = record.getCorpNm() + UNDERLINE_SEPARATOR + record.getCstStdNm();
        if (SBJ_RSLT_CD_CNTL == selfCntlFlag) {
          key = record.getCstStdNm() + UNDERLINE_SEPARATOR + record.getCorpNm();
          // 颠到本方和对方来抵消全部资产，
          if (typeFlag) {
            record.setSbjTpCd("02");
          }
        } else {
          if (typeFlag) {
            record.setSbjTpCd("01");
          }
        }
        if (!resultMap.containsKey(key)) {
          resultMap.put(key, new ArrayList<>());
        }
        resultMap.get(key).add(record);
      });
    }
    return resultMap;
  }

  /**
   * 本方，对方数据比较，并将比较后的数据入库
   *
   * @param hostResultListMap 本方集合
   * @param tradeResultListMap 对方集合
   * @param ruleSbjListMap 科目匹配规则, 按照优先级排序后的规则, 每条规则包含了本方和对方需要使用的科目代码和科目名称
   */
  public AccountCheckExecuteResultDTO processAccountCheckData(
      Map<String, List<AccountOrigin>> hostResultListMap,
      Map<String, List<AccountOrigin>> tradeResultListMap,
      Map<String, List<Set<String>>> ruleSbjListMap) {

    AccountCheckExecuteResultDTO resultDto = new AccountCheckExecuteResultDTO();

    for (Map.Entry<String, List<AccountOrigin>> entry : hostResultListMap.entrySet()) {
      String hostNameKey = entry.getKey();
      List<AccountOrigin> hostCompareList = entry.getValue();

      if (tradeResultListMap.containsKey(hostNameKey)) {

        List<AccountOrigin> tradeCompareList = tradeResultListMap.get(hostNameKey);

        this.accountCheckMatch(hostCompareList, tradeCompareList, ruleSbjListMap, resultDto);

      } else {
        // 没有符合名字映射的对方数据-单边记账
        this.processNameUnMatchAccountToOneSide(hostCompareList, resultDto);
      }
    }

    // 经确认后, 直接没有被本方名称匹配上的对方记录, 记为单边记账
    for (Map.Entry<String, List<AccountOrigin>> entry : tradeResultListMap.entrySet()) {
      String tradeNameKey = entry.getKey();
      if (!hostResultListMap.containsKey(tradeNameKey)) {
        List<AccountOrigin> tradeList = entry.getValue();
        this.processNameUnMatchAccountToOneSide(tradeList, resultDto);
      }
    }

    return resultDto;

  }

  /**
   * 按公司名称, 将本方中直接没有对方数据, 对方中也没有本方数据对应的都记为单边记账
   *
   * @param list
   * @param resultDto
   */
  private void processNameUnMatchAccountToOneSide(List<AccountOrigin> list,
      AccountCheckExecuteResultDTO resultDto) {
    for (AccountOrigin account : list) {
      account.setAmtOfst(new BigDecimal("0.00"));
      account.setDiffEop(account.getAmtEop());
      resultDto.getOneSideList().add(account);
    }
  }



  /**
   * 内部对账算法执行
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param ruleSbjListMap
   * @param resultDto
   */
  public void accountCheckMatch(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList, Map<String, List<Set<String>>> ruleSbjListMap,
      AccountCheckExecuteResultDTO resultDto) {


    for (Map.Entry<String, List<Set<String>>> entry : ruleSbjListMap.entrySet()) {
      String mappingId = entry.getKey();
      logger.info("执行规则:{}", mappingId);

      List<Set<String>> sbjSetList = entry.getValue();
      Set<String> selfSbjSet = sbjSetList.get(SBJ_RSLT_CD_SELF);
      Set<String> cntlSbjSet = sbjSetList.get(SBJ_RSLT_CD_CNTL);

      List<AccountOrigin> hostSbjCompareList =
          this.generateCompareListBySubject(hostCompareList, selfSbjSet);
      List<AccountOrigin> tradeSbjCompareList =
          this.generateCompareListBySubject(tradeCompareList, cntlSbjSet);

      // 1. 先处理相等匹配
      this.accountCheckEqualMatch(hostSbjCompareList, tradeSbjCompareList,
          resultDto.getEqualMatchResultMap(), hostCompareList, tradeCompareList, mappingId);

      // 2. 剩余的接着处理抵消匹配
      this.accountCheckOffsetMatch(hostSbjCompareList, tradeSbjCompareList,
          resultDto.getOffsetMatchResultMap(), hostCompareList, tradeCompareList, mappingId);

    }
    // 3. 所有剩余未被处理且科目在配置的匹配科目中的数据, 都记为单边记账
    // 4. 剩余的没有被科目匹配过的数据, 添加一种状态,"未配置", 显示在内部对账页面的最后, 类似于单边记账, 但是不允许(添加删除手动), 依然可以钻取
    this.accountCheckRemainProcess(hostCompareList, resultDto.getOneSideList(),
        resultDto.getSubUndefinedList());
    this.accountCheckRemainProcess(tradeCompareList, resultDto.getOneSideList(),
        resultDto.getSubUndefinedList());
  }

  /**
   * 按照科目代码和名称, 找到对应的对账结果集
   *
   * @param totalList
   * @param subjectSet
   * @return
   */
  private List<AccountOrigin> generateCompareListBySubject(List<AccountOrigin> totalList,
      Set<String> subjectSet) {
    List<AccountOrigin> targetCompareList = new ArrayList<>();

    for (AccountOrigin account : totalList) {
      String subject = account.getSbjCd() + UNDERLINE_SEPARATOR + account.getSbjNm();
      if (subjectSet.contains(subject)) {
        account.setDefinedBySubject(Boolean.TRUE);
        targetCompareList.add(account.deepClone());
      }
    }

    return targetCompareList;
  }

  /**
   * 相等匹配, 包含1:1, 1:M, N:1, M:N
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   * @param initHostList
   * @param initTradeList
   */
  private void accountCheckEqualMatch(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {

    logger.info("开始相等匹配,本方: {}条, 对方: {}条", hostCompareList.size(), tradeCompareList.size());

    // 相等匹配时,先1:1,
    this.accountCheckEqualMatchOneToOne(hostCompareList, tradeCompareList, equalMatchResultMap,
        initHostList, initTradeList, mappingId);

    // 一对多s
    this.accountCheckEqualMatchOneToMore(hostCompareList, tradeCompareList, equalMatchResultMap,
        initHostList, initTradeList, mappingId);

    // 多对一
    this.accountCheckEqualMatchMoreToOne(hostCompareList, tradeCompareList, equalMatchResultMap,
        initHostList, initTradeList, mappingId);

    logger.info("相等匹配进入多对多,本方剩余: {}条, 对方剩余: {}条", hostCompareList.size(), tradeCompareList.size());

    // 最后多对多
    // Original 多对多时,不考虑复杂的排列组合,只枚举出个数为3,2的组合作为基数
    // Current 多对多, 目前暂时先去所有数据的和, 看是否匹配上
    this.accountCheckEqualMatchMoreToMore(hostCompareList, tradeCompareList, equalMatchResultMap,
        initHostList, initTradeList, mappingId);

  }

  /**
   * 相等匹配一对一
   *C04版本使用，增加关联方匹配
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   */
  private void accountCheckEqualMatchOneToOne(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    Map<BigDecimal, List<AccountOrigin>> singleHostResultListMap =
        this.generateSingleResult(hostCompareList);
    Map<BigDecimal, List<AccountOrigin>> singleTradeResultListMap =
        this.generateSingleResult(tradeCompareList);

    Iterator<BigDecimal> hostKeyIterator = singleHostResultListMap.keySet().iterator();
    while (hostKeyIterator.hasNext()) {
      BigDecimal result = hostKeyIterator.next();
      if (singleTradeResultListMap.containsKey(result)) {
        List<AccountOrigin> hostAccountList = singleHostResultListMap.get(result);
        List<AccountOrigin> tradeAccountList = singleTradeResultListMap.get(result);
        int min = Math.min(hostAccountList.size(), tradeAccountList.size());
        for (int index = 0; index < min; index++) {

          AccountOrigin host = hostAccountList.get(index);
          AccountOrigin trade = tradeAccountList.get(index);
          //在此处进行修改
          
          //在此处进行修改


          List<AccountOrigin> hostResultList = Arrays.asList(host);
          List<AccountOrigin> tradeResultList = Arrays.asList(trade);
          // TO DO Save to temper value
          // ...
          this.saveMatchAccountTemp(hostResultList, tradeResultList, equalMatchResultMap,
              host.getAmtEop(), trade.getAmtEop(), mappingId);

          // remove from compareList and initList, as they are been used.
          this.removeUsedAccountEntry(hostResultList, hostCompareList);
          this.removeUsedAccountEntry(hostResultList, initHostList);
          this.removeUsedAccountEntry(tradeResultList, tradeCompareList);
          this.removeUsedAccountEntry(tradeResultList, initTradeList);
        }
      }
    }
  }


  /**
   * 相等匹配一对一
   * C03版本使用
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   */
  private void accountCheckEqualMatchOneToOne_C03(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    Map<BigDecimal, List<AccountOrigin>> singleHostResultListMap =
        this.generateSingleResult(hostCompareList);
    Map<BigDecimal, List<AccountOrigin>> singleTradeResultListMap =
        this.generateSingleResult(tradeCompareList);

    Iterator<BigDecimal> hostKeyIterator = singleHostResultListMap.keySet().iterator();
    while (hostKeyIterator.hasNext()) {
      BigDecimal result = hostKeyIterator.next();
      if (singleTradeResultListMap.containsKey(result)) {
        List<AccountOrigin> hostAccountList = singleHostResultListMap.get(result);
        List<AccountOrigin> tradeAccountList = singleTradeResultListMap.get(result);
        int min = Math.min(hostAccountList.size(), tradeAccountList.size());
        for (int index = 0; index < min; index++) {

          AccountOrigin host = hostAccountList.get(index);
          AccountOrigin trade = tradeAccountList.get(index);

          List<AccountOrigin> hostResultList = Arrays.asList(host);
          List<AccountOrigin> tradeResultList = Arrays.asList(trade);
          // TO DO Save to temper value
          // ...
          this.saveMatchAccountTemp(hostResultList, tradeResultList, equalMatchResultMap,
              host.getAmtEop(), trade.getAmtEop(), mappingId);

          // remove from compareList and initList, as they are been used.
          this.removeUsedAccountEntry(hostResultList, hostCompareList);
          this.removeUsedAccountEntry(hostResultList, initHostList);
          this.removeUsedAccountEntry(tradeResultList, tradeCompareList);
          this.removeUsedAccountEntry(tradeResultList, initTradeList);
        }
      }
    }
  }

  /**
   * 筛选与本方符号相同的数C04使用
   * @param availableList
   * @param target
   * @return
   */
  private List<AccountOrigin> compareSign(List<AccountOrigin> availableList,
      AccountOrigin target){
    List<AccountOrigin> resultList=new ArrayList<>();
    for(AccountOrigin count:availableList){
      if((count.getAmtEop().multiply(target.getAmtEop())).compareTo(BigDecimal.ZERO)>=0){
        resultList.add(count);
      }
    }
    return resultList;
  }

  /**
   * 相等匹配一对多
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   */
  private void accountCheckEqualMatchOneToMore(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    Map<BigDecimal, List<AccountOrigin>> singleHostResultListMap =
        this.generateSingleResult(hostCompareList);
    for (Map.Entry<BigDecimal, List<AccountOrigin>> hostEntry : singleHostResultListMap
        .entrySet()) {
      List<AccountOrigin> hostAccountList = hostEntry.getValue();
      for (int index = 0; index < hostAccountList.size(); index++) {
        AccountOrigin host = hostAccountList.get(index);
        //C04版本使用compareSign(tradeCompareList, host)，C03版本使用tradeCompareList
        List<AccountOrigin> tradeComboList = this.findOneComboBySum(compareSign(tradeCompareList, host), host);

        if (!CollectionUtils.isEmpty(tradeComboList)) {
          // TO DO Save to temper value
          // ...
          List<AccountOrigin> hostComboList = Arrays.asList(host);
          this.saveMatchAccountTemp(hostComboList, tradeComboList, equalMatchResultMap,
              host.getAmtEop(), host.getAmtEop(), mappingId);

          // remove from compareList, as they are been used.
          this.removeUsedAccountEntry(hostComboList, hostCompareList);
          this.removeUsedAccountEntry(hostComboList, initHostList);
          this.removeUsedAccountEntry(tradeComboList, tradeCompareList);
          this.removeUsedAccountEntry(tradeComboList, initTradeList);
        }
      }
    }
  }

  /**
   * 相等匹配多对多
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   */
  private void accountCheckEqualMatchMoreToOne(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    Map<BigDecimal, List<AccountOrigin>> singleTradeResultListMap =
        this.generateSingleResult(tradeCompareList);
    for (Map.Entry<BigDecimal, List<AccountOrigin>> tradeEntry : singleTradeResultListMap
        .entrySet()) {
      List<AccountOrigin> tradeAccountList = tradeEntry.getValue();

      for (int index = 0; index < tradeAccountList.size(); index++) {
        AccountOrigin trade = tradeAccountList.get(index);

        List<AccountOrigin> hostComboList = this.findOneComboBySum(compareSign(hostCompareList,trade), trade);
        if (!CollectionUtils.isEmpty(hostComboList)) {
          // TO DO Save to temper value
          // ...
          List<AccountOrigin> tradeComboList = Arrays.asList(trade);
          this.saveMatchAccountTemp(hostComboList, tradeComboList, equalMatchResultMap,
              trade.getAmtEop(), trade.getAmtEop(), mappingId);

          // remove from compareList, as they are been used.
          this.removeUsedAccountEntry(hostComboList, hostCompareList);
          this.removeUsedAccountEntry(hostComboList, initHostList);
          this.removeUsedAccountEntry(tradeComboList, tradeCompareList);
          this.removeUsedAccountEntry(tradeComboList, initTradeList);
        }
      }
    }
  }
  /**
   * 多对多相等匹配 目前暂时先去所有数据的和, 看是否匹配上C03使用
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   * @param initHostList
   * @param initTradeList
   */
  private void accountCheckEqualMatchMoreToMore(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    // 多对多, 获取本方和对方的正数及负数的和
    Map<BigDecimal,List<AccountOrigin>> hostTotalListMap=totalListMap(hostCompareList);
    Map<BigDecimal,List<AccountOrigin>> tradeTotalListMap=totalListMap(tradeCompareList);
    for (Map.Entry<BigDecimal, List<AccountOrigin>> hostEntry:hostTotalListMap.entrySet()){
      if(hostEntry.getKey().compareTo(BigDecimal.ZERO)!=0&&tradeTotalListMap.get(hostEntry.getKey())!=null){
        logger.info("多对多相等找到结果: {}, 本方{}条, 对方{}条", hostEntry.getKey(), hostEntry.getValue().size(),
            tradeTotalListMap.get(hostEntry.getKey()).size());
        this.saveMatchAccountTemp(new ArrayList<>(hostEntry.getValue()), new ArrayList<>(tradeTotalListMap.get(hostEntry.getKey())),
            equalMatchResultMap, hostEntry.getKey(), hostEntry.getKey(), mappingId);
        this.removeUsedAccountEntry(hostEntry.getValue(), initHostList);
        this.removeUsedAccountEntry(tradeTotalListMap.get(hostEntry.getKey()), initTradeList);
        this.removeUsedAccountEntry(hostEntry.getValue(), hostCompareList);
        this.removeUsedAccountEntry(tradeTotalListMap.get(hostEntry.getKey()), tradeCompareList);
      }
    }
  }

  /**
   * 获取整数和负数的和
   * @param countList
   * @return
   */
  private Map<BigDecimal,List<AccountOrigin>> totalListMap(List<AccountOrigin> countList){
    //正数的和
    BigDecimal rightTotal=new BigDecimal(0.00);
    //负数的和
    BigDecimal lossTotal=new BigDecimal(0.00);
    //正数的List
    List<AccountOrigin> rightTotallList=new ArrayList<>();
    //负数的List
    List<AccountOrigin> lossTotalList=new ArrayList<>();
    Map<BigDecimal,List<AccountOrigin>> totalListMap=new HashMap<>();
    for (AccountOrigin account:countList){
      if(account.getAmtEop().compareTo(BigDecimal.ZERO)>=0){
        rightTotal=rightTotal.add(account.getAmtEop());
        rightTotallList.add(account);
      }else {
        lossTotal=lossTotal.add(account.getAmtEop());
        lossTotalList.add(account);
      }
    }
    totalListMap.put(rightTotal,rightTotallList);
    totalListMap.put(lossTotal,lossTotalList);
    return totalListMap;
  }

  /**
   * 多对多相等匹配 目前暂时先去所有数据的和, 看是否匹配上C03使用
   *
   * @param hostCompareList
   * @param tradeCompareList
   * @param equalMatchResultMap
   * @param initHostList
   * @param initTradeList
   */
  private void accountCheckEqualMatchMoreToMore_Tim_C03(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    // 多对多, 目前暂时先去所有数据的和, 看是否匹配上
    BigDecimal hostTotal = calculateTotal(hostCompareList);
    BigDecimal tradeTotal = calculateTotal(tradeCompareList);
    if (hostTotal.compareTo(BigDecimal.ZERO) > 0 && tradeTotal.compareTo(BigDecimal.ZERO) > 0
        && hostTotal.compareTo(tradeTotal) == 0) {
      logger.info("多对多相等找到结果: {}, 本方{}条, 对方{}条", hostTotal, hostCompareList.size(),
          tradeCompareList.size());
      this.saveMatchAccountTemp(new ArrayList<>(hostCompareList), new ArrayList<>(tradeCompareList),
          equalMatchResultMap, hostTotal, tradeTotal, mappingId);
      // remove from compareList, as they are been used.
      this.removeUsedAccountEntry(hostCompareList, initHostList);
      this.removeUsedAccountEntry(tradeCompareList, initTradeList);
      hostCompareList.clear();
      tradeCompareList.clear();
    } else {
      logger.info("没有找到多对多相等的集合结果");
    }
  }

  /**
   * 计算金额总和
   *
   * @param compareList
   * @return
   */
  public BigDecimal calculateTotal(List<AccountOrigin> compareList) {
    BigDecimal total = BigDecimal.ZERO;
    if (!CollectionUtils.isEmpty(compareList)) {
      for (AccountOrigin account : compareList) {
        total = total.add(account.getAmtEop());
      }
    }
    return total;
  }



  /**
   * 抵小抵消匹配C04版本使用
   *
   * @param hostCompareList
   * @param tradeCompareList
   */
  private void accountCheckOffsetMatch(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> offsetMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId){

    Map<BigDecimal,List<AccountOrigin>> hostTotalListMap=totalListMap(hostCompareList);
    Map<BigDecimal,List<AccountOrigin>> tradeTotalListMap=totalListMap(tradeCompareList);
    for(Map.Entry<BigDecimal, List<AccountOrigin>> hostEntry:hostTotalListMap.entrySet()){
      if(hostEntry.getKey().compareTo(BigDecimal.ZERO)==0){
        continue;
      }
      if (hostEntry.getKey().compareTo(BigDecimal.ZERO)>0){
        for(Map.Entry<BigDecimal, List<AccountOrigin>> tradeEntry:tradeTotalListMap.entrySet()){
          if((hostEntry.getKey().multiply(tradeEntry.getKey())).compareTo(BigDecimal.ZERO)>0){
            accountCheckOffsetMatch_compare(new AccountCompareParam(hostEntry,tradeEntry,true),
                hostCompareList, tradeCompareList, offsetMatchResultMap, initHostList, initTradeList,
                mappingId);
          }
        }
      }else {
        for(Map.Entry<BigDecimal, List<AccountOrigin>> tradeEntry:tradeTotalListMap.entrySet()){
          if((hostEntry.getKey().multiply(tradeEntry.getKey())).compareTo(BigDecimal.ZERO)>0){
            accountCheckOffsetMatch_compare(new AccountCompareParam(hostEntry,tradeEntry,false),
                hostCompareList, tradeCompareList, offsetMatchResultMap, initHostList, initTradeList,
                mappingId);
          }
        }
      }
    }

//    accountCheckOffsetMatch_compare(hostEntry,hostCompareList, tradeCompareList, offsetMatchResultMap, initHostList, initTradeList,
//        mappingId);

  }

  /**
   * 抵小比对
   * @param hostCompareList
   * @param tradeCompareList
   * @param offsetMatchResultMap
   * @param initHostList
   * @param initTradeList
   * @param mappingId
   */
  private void accountCheckOffsetMatch_compare(AccountCompareParam accountCompareParam,
      List<AccountOrigin> hostCompareList, List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> offsetMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    BigDecimal hostTotal=accountCompareParam.getHostEntry().getKey();
    BigDecimal tradeTotal=accountCompareParam.getTradeEntry().getKey();
    boolean flag=hostTotal.compareTo(tradeTotal)<0;
    flag=accountCompareParam.isRight()?flag:!flag;
    AccountOrigin targetAccount=new AccountOrigin();
    targetAccount.setAmtEop(flag?hostTotal:tradeTotal);
    Map<BigDecimal, List<AccountOrigin>> targetTradeListMap =
        this.generateOneToMoreOffsetCombo(targetAccount, flag?accountCompareParam.getTradeEntry().getValue():accountCompareParam.getHostEntry().getValue(),
            true,accountCompareParam.isRight());
    if (!CollectionUtils.isEmpty(targetTradeListMap)) {

      BigDecimal tradeSum = (targetTradeListMap.keySet().toArray(new BigDecimal[0]))[0];
      List<AccountOrigin> targetTradeList = targetTradeListMap.get(tradeSum);
      //本方科目总额小于对方总额
      if(flag){
        List<AccountOrigin> targetList = new ArrayList<>();
        targetList.addAll(accountCompareParam.getHostEntry().getValue());
        this.saveMatchAccountTemp(targetList, targetTradeList, offsetMatchResultMap,
            hostTotal, tradeSum, mappingId);
        this.removeUsedAccountEntry(targetList, hostCompareList);
        this.removeUsedAccountEntry(targetList, initHostList);
        this.removeUsedAccountEntry(targetTradeList, tradeCompareList);
        this.removeUsedAccountEntry(targetTradeList, initTradeList);
      }else {
        List<AccountOrigin> targetList = new ArrayList<>();
        targetList.addAll(accountCompareParam.getTradeEntry().getValue());
        this.saveMatchAccountTemp(targetList,targetTradeList,  offsetMatchResultMap,
            tradeTotal, tradeSum, mappingId);
        this.removeUsedAccountEntry(targetTradeList, hostCompareList);
        this.removeUsedAccountEntry(targetTradeList, initHostList);
        this.removeUsedAccountEntry(targetList, tradeCompareList);
        this.removeUsedAccountEntry(targetList, initTradeList);
      }
    }
  }

  /**
   * 获取抵小科目和
   * @param originList
   * @return
   */
  private BigDecimal getTotalEmtEop(List<AccountOrigin> originList){
    if(CollectionUtils.isEmpty(originList)){
      return null;
    }
    BigDecimal total=new BigDecimal(0.00);
    for(AccountOrigin accountOrigin : originList){
      total=total.add(accountOrigin.getAmtEop());
    }
    return total;
  }

  /**
   * 抵小抵消匹配C03版本使用
   *
   * @param hostCompareList
   * @param tradeCompareList
   */
  private void accountCheckOffsetMatch_Old_Tim(List<AccountOrigin> hostCompareList,
      List<AccountOrigin> tradeCompareList,
      Map<String, Map<String, List<AccountOrigin>>> offsetMatchResultMap,
      List<AccountOrigin> initHostList, List<AccountOrigin> initTradeList, String mappingId) {
    logger.info("开始抵小匹配,本方: {}条, 对方: {}条", hostCompareList.size(), tradeCompareList.size());
    // 1对多抵小
    Map<BigDecimal, List<AccountOrigin>> singleHostResultListMap =
        this.generateSingleResult(hostCompareList);

    for (Map.Entry<BigDecimal, List<AccountOrigin>> hostEntry : singleHostResultListMap
        .entrySet()) {
      List<AccountOrigin> hostAccountList = hostEntry.getValue();
      for (int index = 0; index < hostAccountList.size(); index++) {
        AccountOrigin host = hostAccountList.get(index);
        // 为了减少匹配的复杂度, 这里先过滤出金额小于本方金额的对方数据
        List<AccountOrigin> tradeList = pickSuitableList(host, tradeCompareList);

        Map<BigDecimal, List<AccountOrigin>> targetTradeListMap =
            this.generateOneToMoreOffsetCombo(host, tradeList, true,true);

        if (!CollectionUtils.isEmpty(targetTradeListMap)) {
          // TO DO Save to temper value
          // ...

          List<AccountOrigin> targetHostList = Arrays.asList(host);

          BigDecimal tradeSum = (targetTradeListMap.keySet().toArray(new BigDecimal[0]))[0];
          List<AccountOrigin> targetTradeList = targetTradeListMap.get(tradeSum);

          this.saveMatchAccountTemp(targetHostList, targetTradeList, offsetMatchResultMap,
              host.getAmtEop(), tradeSum, mappingId);

          // remove from compareList and initList, as they are been used.
          this.removeUsedAccountEntry(targetHostList, hostCompareList);
          this.removeUsedAccountEntry(targetHostList, initHostList);
          this.removeUsedAccountEntry(targetTradeList, tradeCompareList);
          this.removeUsedAccountEntry(targetTradeList, initTradeList);
        }

      }
    }

    // 多对一抵小
    Map<BigDecimal, List<AccountOrigin>> singleTradeResultListMap =
        this.generateSingleResult(tradeCompareList);
    for (Map.Entry<BigDecimal, List<AccountOrigin>> tradeEntry : singleTradeResultListMap
        .entrySet()) {
      List<AccountOrigin> tradeAccountList = tradeEntry.getValue();
      for (int index = 0; index < tradeAccountList.size(); index++) {
        AccountOrigin trade = tradeAccountList.get(index);

        Map<BigDecimal, List<AccountOrigin>> targetHostListMap =
            this.generateOneToMoreOffsetCombo(trade, hostCompareList, false,true);

        if (!CollectionUtils.isEmpty(targetHostListMap)) {

          BigDecimal hostSum = (targetHostListMap.keySet().toArray(new BigDecimal[0]))[0];
          List<AccountOrigin> targetHostList = targetHostListMap.get(hostSum);

          List<AccountOrigin> targetTradeList = Arrays.asList(trade);

          this.saveMatchAccountTemp(targetHostList, targetTradeList, offsetMatchResultMap, hostSum,
              trade.getAmtEop(), mappingId);

          // remove from compareList, as they are been used.
          this.removeUsedAccountEntry(targetHostList, hostCompareList);
          this.removeUsedAccountEntry(targetHostList, initHostList);
          this.removeUsedAccountEntry(targetTradeList, tradeCompareList);
          this.removeUsedAccountEntry(targetTradeList, initTradeList);
        }
      }
    }
  }

  /**
   * 剩余数据状态处理
   *
   * @param compareList
   * @param oneSideList
   * @param subUndefinedList
   */
  public void accountCheckRemainProcess(List<AccountOrigin> compareList,
      List<AccountOrigin> oneSideList, List<AccountOrigin> subUndefinedList) {
    // 根据AccountOrigin里的definedBySubject属性来确定剩余条目的状态
    // 如果definedBySubject为True, 代表在科目匹配中定义过, 但是经过相等和抵小之后依然没有被处理的数据, 这些数据状态记过"单边记账"
    // 如果definedBySubject为False, 代表没有在科目匹配中被定义, 这类数据状态为 "未配置"
    for (AccountOrigin account : compareList) {
      AccountOrigin target = account.deepClone();
      target.setAmtOfst(new BigDecimal("0.00"));
      target.setDiffEop(target.getAmtEop());
      if (account.getDefinedBySubject()) {
        oneSideList.add(target);
      } else {
        subUndefinedList.add(target);
      }
    }
  }

  /**
   * 返回所有单个数据的结果, 可能相同的结果会有多个数据, 使用TreeMap将结果从小到大排序
   *
   * @param compareList
   * @return
   */
  private Map<BigDecimal, List<AccountOrigin>> generateSingleResult(
      List<AccountOrigin> compareList) {
    Map<BigDecimal, List<AccountOrigin>> singleResultListMap =
        new TreeMap<>((BigDecimal o1, BigDecimal o2) -> o1.compareTo(o2));
    for (AccountOrigin account : compareList) {
      BigDecimal result = account.getAmtEop();
      if (!singleResultListMap.containsKey(result)) {
        singleResultListMap.put(result, new ArrayList<AccountOrigin>());
      }
      singleResultListMap.get(result).add(account);
    }
    return singleResultListMap;
  }

  /**
   * 排列组合算法, 结果从小打到排序
   *
   * @param list
   * @param target
   * @param isFromSelf
   * @return
   */
  public Map<BigDecimal, List<List<AccountOrigin>>> generateComboListMap(List<AccountOrigin> list,
      BigDecimal target, boolean isFromSelf,boolean isRight) {
    Map<BigDecimal, List<List<AccountOrigin>>> resultMap =
        new TreeMap<>((BigDecimal o1, BigDecimal o2) -> o1.compareTo(o2));
    if(!isRight){
      resultMap=new TreeMap<>((BigDecimal o1, BigDecimal o2) -> o2.compareTo(o1));
    }
    long n = (long) Math.pow(2, list.size());
    BigDecimal sumResult = null;
    List<AccountOrigin> resultList = null;
    for (long l = 0L; l < n; l++) {
      sumResult = BigDecimal.ZERO;
      resultList = new ArrayList<>();
      for (int i = 0; i < list.size(); i++) {
        if ((l >>> i & 1) == 1) {
          AccountOrigin detail = list.get(i);
          resultList.add(detail);
          sumResult = sumResult.add(detail.getAmtEop());
        }
      }

      //C03版本本方找对方的时候是只获取大于target金额的数据组合，C04版本进行修改
      if (resultList.isEmpty() || (isFromSelf && sumResult.abs().compareTo(target.abs()) <= 0)) {
        // 本方找对方的时候, 只获取大于target金额的数据组合
        continue;
      }

      if (!resultMap.containsKey(sumResult)) {
        resultMap.put(sumResult, new ArrayList<>());
      }
      resultMap.get(sumResult).add(resultList);

    }
    return resultMap;
  }

  /**
   * 在集合中, 找到任意一组和为给定值的组合
   *
   * @param availableList
   * @param sumAccount
   * @return
   */
  public List<AccountOrigin> findOneComboBySum(List<AccountOrigin> availableList,
      AccountOrigin sumAccount) {
    logger.info("查找结果相等的集合, 合计为: {}", sumAccount.getAmtEop());
    int size = availableList.size();
    List<AccountOrigin> resultList = null;
    for (int i = 1; i < 1 << size; i++) {
      BigDecimal sum = new BigDecimal("0");
      resultList = new ArrayList<>();
      for (int j = 0; j < size; j++) {
        if ((i & 1 << j) != 0) {
          AccountOrigin detail = availableList.get(j);
          sum = sum.add(detail.getAmtEop());
          resultList.add(detail);
        }
      }
      if (sum.compareTo(sumAccount.getAmtEop()) == 0) {
        logger.info("结束并找到集合的结果为: {}, 组合条数: {}", sum, resultList.size());
        return resultList;
      }
    }
    logger.info("没有找到集合的合计结果为: {}", sumAccount.getAmtEop());
    return new ArrayList<>();
  }

  /**
   * 该方法暂时不用
   *
   * @param accountList
   * @return
   */
  public Map<BigDecimal, List<List<AccountOrigin>>> generateMore2MoreComboMap(
      List<AccountOrigin> accountList) {
    if (CollectionUtils.isEmpty(accountList)) {
      return null;
    }

    Map<BigDecimal, List<List<AccountOrigin>>> resultListMap =
        new TreeMap<>((BigDecimal o1, BigDecimal o2) -> o2.compareTo(o1));
    for (int i = 0, size = accountList.size(); i < size; i++) {
      AccountOrigin firstLevel = accountList.get(i);
      for (int j = i + 1; j < size; j++) {
        AccountOrigin secondLevel = accountList.get(j);
        BigDecimal sum12 = firstLevel.getAmtEop().add(secondLevel.getAmtEop());
        presetKeyToMap(resultListMap, sum12);
        resultListMap.get(sum12).add(Arrays.asList(firstLevel, secondLevel));
        for (int z = j + 1; z < size; z++) {
          AccountOrigin thirdLevel = accountList.get(z);
          BigDecimal sum13 = firstLevel.getAmtEop().add(thirdLevel.getAmtEop());
          presetKeyToMap(resultListMap, sum13);
          resultListMap.get(sum13).add(Arrays.asList(firstLevel, thirdLevel));

          BigDecimal sum23 = secondLevel.getAmtEop().add(thirdLevel.getAmtEop());
          presetKeyToMap(resultListMap, sum23);
          resultListMap.get(sum23).add(Arrays.asList(secondLevel, thirdLevel));

          BigDecimal sum123 =
              firstLevel.getAmtEop().add(secondLevel.getAmtEop()).add(thirdLevel.getAmtEop());
          presetKeyToMap(resultListMap, sum123);
          resultListMap.get(sum123).add(Arrays.asList(firstLevel, secondLevel, thirdLevel));

        }
      }
    }
    return resultListMap;
  }

  /**
   *
   * @param resultListMap
   * @param key
   */
  private void presetKeyToMap(Map<BigDecimal, List<List<AccountOrigin>>> resultListMap,
      BigDecimal key) {
    if (!resultListMap.containsKey(key)) {
      resultListMap.put(key, new ArrayList<>());
    }
  }

  /**
   * 获取一对多,多对一抵小抵消情况最符合的组合
   *
   * @param target
   * @param sourceList
   * @param isFromSelf
   * @return
   */
  private Map<BigDecimal, List<AccountOrigin>> generateOneToMoreOffsetCombo(AccountOrigin target,
      List<AccountOrigin> sourceList, boolean isFromSelf,boolean isRight) {
    Map<BigDecimal, List<AccountOrigin>> resultListMap = null;
    if (target == null || CollectionUtils.isEmpty(sourceList)) {
      return resultListMap;
    }

    logger.info("{} 查找抵小的集合, 合计为: {}", isFromSelf ? "本方->对方1:多" : "本方->对方多:1", target.getAmtEop());
    // 获取组合的集合, 集合按照求和从小到大排序
    Map<BigDecimal, List<List<AccountOrigin>>> tradeResultListMap =
        generateComboListMap(sourceList, target.getAmtEop(), isFromSelf,isRight);

    BigDecimal tempKey = null;
    for (Map.Entry<BigDecimal, List<List<AccountOrigin>>> entry : tradeResultListMap.entrySet()) {
      BigDecimal sumKey = entry.getKey();
      if(!isRight&&sumKey.abs().compareTo(target.getAmtEop().abs())>0){
        tempKey = sumKey;
        break;
      }
      if (sumKey.compareTo(target.getAmtEop()) > 0) {
        tempKey = sumKey;
        break;
      }
    }

    if (tempKey != null) {
      List<List<AccountOrigin>> offsetTradeListList = tradeResultListMap.get(tempKey);
      resultListMap = new HashMap<>();
      resultListMap.put(tempKey, new ArrayList<>(offsetTradeListList.get(0)));
      logger.info("结束并找到集合的结果为: {}, 组合{}条", tempKey, offsetTradeListList.get(0).size());
      for (AccountOrigin account : offsetTradeListList.get(0)) {
        logger.info("组合结果细节: {}", account.getAmtEop());
      }
    } else {
      logger.info("没有找到符合:{}的抵小集合", target.getAmtEop());
    }

    return resultListMap;
  }

  /**
   * 查找金额小于目标的集合
   *
   * @param origin
   * @param targetList
   * @return
   */
  private List<AccountOrigin> pickSuitableList(AccountOrigin origin,
      List<AccountOrigin> targetList) {
    List<AccountOrigin> resultList = new ArrayList<>();
    BigDecimal amtEop = origin.getAmtEop();
    for (AccountOrigin target : targetList) {
      if (target.getAmtEop().compareTo(amtEop) < 0) {
        resultList.add(target.deepClone());
      }
    }
    return resultList;
  }

  /**
   * 将被使用的数据从源头删除
   *
   * @param usedAccountList
   * @param originAccountList
   * @return
   */
  private boolean removeUsedAccountEntry(List<AccountOrigin> usedAccountList,
      List<AccountOrigin> originAccountList) {
    if (CollectionUtils.isEmpty(usedAccountList) || CollectionUtils.isEmpty(originAccountList)) {
      return false;
    }
    return originAccountList.removeAll(usedAccountList);
  }

  /**
   * 将计算好的匹配结果(本方和对方)存储到临时的集合中
   *
   * @param hostAccountList
   * @param tradeAccountList
   * @param tempResultListMap Map<uuid, Map<本方/对方, List<AccountOrigin>结果集>>
   */
  private void saveMatchAccountTemp(List<AccountOrigin> hostAccountList,
      List<AccountOrigin> tradeAccountList,
      Map<String, Map<String, List<AccountOrigin>>> tempResultListMap, BigDecimal hostTotal,
      BigDecimal tradeTotal, String mappingId) {
    if (tempResultListMap == null) {
      return;
    }

    String uuid = this.getUUID();
    Map<String, List<AccountOrigin>> resultMap = new HashMap<>();
    BigDecimal diffTotal = tradeTotal.subtract(hostTotal);

    for (AccountOrigin account : hostAccountList) {
      // 本方数据, AMT_OFST是对方数据的金额和, AMT_DIF是本方的金额合计减去对方数据的金额合计
      account.setAmtOfst(account.getAmtEop());
      account.setDiffEop(new BigDecimal("0.00"));
      account.setMappingId(mappingId);
    }
    resultMap.put(SBJ_RSLT_CD_CNTL + "", hostAccountList);

    for (AccountOrigin account : tradeAccountList) {
      // 对方数据, AMT_OFST直接使用AMT_EOP, AMT_DIF直接是0
      account.setAmtOfst(hostTotal);
      account.setDiffEop(diffTotal);
      account.setMappingId(mappingId);
    }
    resultMap.put(SBJ_RSLT_CD_SELF + "", tradeAccountList);

    tempResultListMap.put(uuid, resultMap);
  }

  /**
   * 针对JUnit Test case获取数据
   *
   * @param corpNm
   * @param cstNm
   * @param year
   * @param month
   * @param type
   * @return
   */
  public List<AccountOrigin> fetchTestData(String corpNm, String cstNm, String year, String month,
      String type) {
    return accountCheckHandlerMapper.selectDataListUT(corpNm, cstNm, year, month, type);
  }

  /**
   * 将内部对账的结果从内存中保存到数据库结果表
   * @param projectId 项目Id
   * @param type 类型
   * @param resultDto 结果集
   */
  public void processResultToDB(String projectId, String type,
      AccountCheckExecuteResultDTO resultDto) {
    logger.info("开始保存内部对账到结果表 项目: {}, 类型: {}", projectId, type);

    int batchSize = 500;

    Map<String, Object> resultMap = this.generateChkactPrtList(resultDto);

    List<KpmgChkactPrt> insertResultList = (List<KpmgChkactPrt>) resultMap.get("0");
    List<KpmgChkactSrcdtl> updateResultList = (List<KpmgChkactSrcdtl>) resultMap.get("1");

    boolean isInsertSuccess =
        iKpmgChkactPrtService.insertChkactPrtDataByBatch(projectId, insertResultList, batchSize);
    boolean isUpdateSuccess =
        iKpmgChkactSrcdtlService.updateSrcdtlData(projectId, updateResultList, batchSize);

    if (isInsertSuccess && isUpdateSuccess) {
      logger.info("内部对账算法对账结果数据入库成功！对账类型: {}", type);
    } else {
      logger.info("内部对账算法对账结果数据入库失败！类型: {}", type);
    }

  }

  private Map<String, Object> generateChkactPrtList(AccountCheckExecuteResultDTO resultDto) {
    Map<String, Object> resultMap = new HashMap<>();
    List<KpmgChkactPrt> resultList = new ArrayList<>();
    List<KpmgChkactSrcdtl> updateList = new ArrayList<>();

    // 处理相等匹配
    Map<String, Map<String, List<AccountOrigin>>> equalMatchResultMap =
        resultDto.getEqualMatchResultMap();
    this.addResultBeanToList(resultList, updateList, equalMatchResultMap, MATCH_AUTO_EQUAL);

    // 处理抵小抵消
    Map<String, Map<String, List<AccountOrigin>>> offsetMatchResultMap =
        resultDto.getOffsetMatchResultMap();
    this.addResultBeanToList(resultList, updateList, offsetMatchResultMap, MATCH_AUTO_OFFSET);

    // 处理单边记账
    logger.info("内部对账保存单边记账数据:{}", resultDto.getOneSideList().size());
    this.addResultBeanToList(resultList, updateList, resultDto.getOneSideList(),
        MATCH_UNILATERAL_ACCOUNT, SBJ_RSLT_CD_SELF);

    // 处理科目未匹配
    logger.info("内部对账保存科目未匹配数据:{}", resultDto.getSubUndefinedList().size());
    this.addResultBeanToList(resultList, updateList, resultDto.getSubUndefinedList(),
        MATCH_SUBJECT_UNDEFINED, SBJ_RSLT_CD_SELF);

    resultMap.put("0", resultList);
    resultMap.put("1", updateList);

    return resultMap;
  }

  private void addResultBeanToList(List<KpmgChkactPrt> resultList,
      List<KpmgChkactSrcdtl> updateList,
      Map<String, Map<String, List<AccountOrigin>>> accountCheckResultMap, String mtchTpNm) {

    String typeName = "相等匹配";
    if (MATCH_AUTO_OFFSET.equals(mtchTpNm)) {
      typeName = "抵小抵消";
    }

    AccountCheckResultSaveParamDTO parameterBean = null;
    BigDecimal totalValue = null;
    int count = 0;

    for (Map.Entry<String, Map<String, List<AccountOrigin>>> entry : accountCheckResultMap
        .entrySet()) {
      String btchId = entry.getKey();
      logger.info("insert {} data with batchId: {}", typeName, btchId);
      Map<String, List<AccountOrigin>> offsetMatchResult = entry.getValue();
      List<AccountOrigin> hostList = offsetMatchResult.get(SBJ_RSLT_CD_SELF + "");
      List<AccountOrigin> tradeList = offsetMatchResult.get(SBJ_RSLT_CD_CNTL + "");

      totalValue = hostList.get(0).getDiffEop();
      parameterBean = new AccountCheckResultSaveParamDTO();
      parameterBean.setMatchBtchId(btchId);
      parameterBean.setMatchTotalValue(totalValue);
      parameterBean.setMatchTypeCode(mtchTpNm);
      parameterBean.setRsltCd(SBJ_RSLT_CD_SELF);

      for (AccountOrigin account : hostList) {
        resultList.add(this.convertResult(account, parameterBean));
        updateList.add(this.convertUpdateResult(account, btchId));
      }

      parameterBean.setRsltCd(SBJ_RSLT_CD_CNTL);
      for (AccountOrigin account : tradeList) {
        resultList.add(this.convertResult(account, parameterBean));
        updateList.add(this.convertUpdateResult(account, btchId));
      }

      count += hostList.size();
      count += tradeList.size();
    }

    logger.info("totally {} records for {} generated", count, typeName);
  }

  private void addResultBeanToList(List<KpmgChkactPrt> resultList,
      List<KpmgChkactSrcdtl> updateList, List<AccountOrigin> list, String mtchTpNm, int rsltCd) {
    AccountCheckResultSaveParamDTO parameterBean = new AccountCheckResultSaveParamDTO();
    parameterBean.setMatchTypeCode(mtchTpNm);
    parameterBean.setRsltCd(rsltCd);
    for (AccountOrigin account : list) {
      resultList.add(this.convertResult(account, parameterBean));
      updateList.add(this.convertUpdateResult(account, account.getDtlId()));
    }
  }

  private KpmgChkactPrt convertResult(AccountOrigin account,
      AccountCheckResultSaveParamDTO parameterBean) {
    BigDecimal total = account.getDiffEop();
    BigDecimal matchTotal = null;
    String mtchBtchId = null;
    String mtchTpNm = null;
    int rsltCd = 0;
    if (parameterBean != null && parameterBean.getMatchTotalValue() != null) {
      matchTotal = parameterBean.getMatchTotalValue();
    }
    if (parameterBean != null && parameterBean.getMatchBtchId() != null) {
      mtchBtchId = parameterBean.getMatchBtchId();
    }
    if (parameterBean != null && parameterBean.getMatchTypeCode() != null) {
      mtchTpNm = parameterBean.getMatchTypeCode();
    }
    if (parameterBean != null) {
      rsltCd = parameterBean.getRsltCd();
    }
    if (matchTotal != null) {
      total = matchTotal;
    }
    KpmgChkactPrt element = new KpmgChkactPrt();
    element.setAcgYr(account.getAcgYr());
    element.setAcgMo(account.getAcgMo());
    element.setCorpCd(account.getCorpCd());
    element.setCorpNm(account.getCorpNm());
    element.setCstCd(account.getCstCd());
    element.setCstNm(account.getCstNm());
    element.setSbjCd(account.getSbjCd());
    element.setSbjNm(account.getSbjNm());
    element.setSbjLvl(account.getSbjLvl());
    element.setSbjTpCd(account.getSbjTpCd());
    element.setSbjTpNm(account.getSbjTpNm());
    element.setAmtEop(account.getAmtEop().doubleValue());
    element.setAmtOfst(account.getAmtOfst().doubleValue());
    element.setMtchTpNm(mtchTpNm);
    element.setMtchRmrk("");
    element.setMtchBtchId(mtchBtchId != null ? mtchBtchId : account.getDtlId());
    element.setAmtDif(account.getDiffEop().doubleValue());
    element.setRsltCd(Double.valueOf(rsltCd + ""));
    element.setAmtEopAcm(total.doubleValue());
    element.setAmtOfstAcm(total.doubleValue());
    element.setMtchTpNmInit(mtchTpNm);
    element.setCstStdNm(account.getCstStdNm());
    element.setMappingId(account.getMappingId());
    return element;
  }

  private KpmgChkactSrcdtl convertUpdateResult(AccountOrigin account, String mtchBtchId) {
    KpmgChkactSrcdtl element = new KpmgChkactSrcdtl();
    element.setDtlId(account.getDtlId());
    element.setMtchInd(KPMG_SRCDTL_MTCH_IND_MATCH);
    element.setMtchBtchId(mtchBtchId);
    return element;
  }

  /**
   * 获取钻取明细余额帐动态数据表头
   *
   * @param projectId 项目Id
   * @param rptType 报告类型, 请直接使用枚举 ReportTypeEnum.JOURNAL_IR.getCode()
   * @return ServerResponse<ArrayList<GeneralHeaderVO>>
   */
  @Override
  public ServerResponse<ArrayList<GeneralHeaderVO>> getJournalDetailTableHeader(String projectId,
      String rptType) {
    logger.info("get the table header...");
    List<GeneralHeaderVO> generalHeaderVOS;
    try {
      ProjectRptSql projectRptSql =
          iProjectRptSqlService.getProjectRptSqlByInfo(projectId, rptType);
      String rptSqlId = projectRptSql.getRptSqlId();
      generalHeaderVOS = iPrjRptSqlMpngService.selectGeneralHeader(projectId, rptSqlId, "zh_cn");// 使用zh_cn代替ContextHolderUtils.getLocale().toString()
      return ServerResponse.createBySuccess(new ArrayList<>(generalHeaderVOS));
    } catch (Exception e) {
      logger.error("获取钻取明细列表动态表头失败", e);
      return ServerResponse.createByErrorMessage("钻取明细列表动态表头获取失败");
    }
  }

  /**
   * 获取余额帐明细数据
   *
   * @param currentPage 分页页数
   * @param pageSize 分页大小
   * @param projectId 项目Id
   * @param rptType 报告类型, 请直接使用枚举 ReportTypeEnum.JOURNAL_IR.getCode()
   * @param matchBatchId 匹配Id
   * @return ServerResponse<Page<Map<String, Object>>>
   */
  @Override
  public ServerResponse<Page<Map<String, Object>>> getAccountCheckJournalDetail(int currentPage,
      int pageSize, String projectId, String rptType, String matchBatchId) {
    try {
      String sql = this.getJournalDetailDynamicSqlText(projectId, rptType);
      logger.info("get sql: {}", sql);
      Page<Map<String, Object>> result = ((AccountCheckServiceImpl) AopContext.currentProxy())
          .getJournalDetailDataBySql(currentPage, pageSize, projectId, matchBatchId, sql);
      return ServerResponse.createBySuccess(result);
    } catch (Exception e) {
      logger.error("明细列表数据失败", e);
      return ServerResponse.createByErrorMessage("钻取明细获取数据失败");
    }
  }

  /**
   * 获取查询明细的SQL
   *
   * @param projectId
   * @param rptType
   * @return String
   */
  public String getJournalDetailDynamicSqlText(String projectId, String rptType) {
    return iProjectRptSqlService.getSqlTextByInfo(projectId, rptType);
  }

  /**
   * 通过sql，分页查询明细数据
   *
   * @param currentPage
   * @param pageSize
   * @param projectId
   * @param matchBatchId
   * @param sql
   * @return Page<Map<String, Object>>
   */
  @DynamicDS("projectId")
  public Page<Map<String, Object>> getJournalDetailDataBySql(int currentPage, int pageSize,
      String projectId, String matchBatchId, String sql) {
    Map<String, Object> parameterMap = new HashMap<>();
    parameterMap.put("MTCH_BTCH_ID", matchBatchId);
    Page<Map<String, Object>> page = new Page<>(currentPage, pageSize);
    return commonExecuteService.queryPageByMap(projectId, page, sql, parameterMap);
  }

}
